From ede38d4cb72c73549d6675009ec07a36fba0fe7d Mon Sep 17 00:00:00 2001 From: Corvin Szimion Date: Thu, 24 Oct 2024 10:55:03 +0200 Subject: [PATCH 01/58] Cleanup of launchSettings and Cards Page in Demo --- src/MainDemo.Wpf/Cards.xaml | 16 ---------------- src/MainDemo.Wpf/Properties/launchSettings.json | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/MainDemo.Wpf/Cards.xaml b/src/MainDemo.Wpf/Cards.xaml index 236182eefb..c04fd8251f 100644 --- a/src/MainDemo.Wpf/Cards.xaml +++ b/src/MainDemo.Wpf/Cards.xaml @@ -24,22 +24,6 @@ - - - - - - - - - - - diff --git a/src/MainDemo.Wpf/Properties/launchSettings.json b/src/MainDemo.Wpf/Properties/launchSettings.json index d7f7960d01..3b8cbc0041 100644 --- a/src/MainDemo.Wpf/Properties/launchSettings.json +++ b/src/MainDemo.Wpf/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Demo App": { "commandName": "Project", - "commandLineArgs": "-p Card -t Light -f LeftToRight" + "commandLineArgs": "-p Home -t Inherit -f LeftToRight" } } } From 8c8644aa726ad8751b3ecd942a1b7bb8ff600109 Mon Sep 17 00:00:00 2001 From: Corvin Szimion Date: Thu, 24 Oct 2024 11:04:43 +0200 Subject: [PATCH 02/58] Revert "Cleanup of launchSettings and Cards Page in Demo" This reverts commit ede38d4cb72c73549d6675009ec07a36fba0fe7d. --- src/MainDemo.Wpf/Cards.xaml | 16 ++++++++++++++++ src/MainDemo.Wpf/Properties/launchSettings.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/MainDemo.Wpf/Cards.xaml b/src/MainDemo.Wpf/Cards.xaml index c04fd8251f..236182eefb 100644 --- a/src/MainDemo.Wpf/Cards.xaml +++ b/src/MainDemo.Wpf/Cards.xaml @@ -24,6 +24,22 @@ + + + + + + + + + + + diff --git a/src/MainDemo.Wpf/Properties/launchSettings.json b/src/MainDemo.Wpf/Properties/launchSettings.json index 3b8cbc0041..d7f7960d01 100644 --- a/src/MainDemo.Wpf/Properties/launchSettings.json +++ b/src/MainDemo.Wpf/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Demo App": { "commandName": "Project", - "commandLineArgs": "-p Home -t Inherit -f LeftToRight" + "commandLineArgs": "-p Card -t Light -f LeftToRight" } } } From 8228167ea35f61fb5f1e189735bbb7ecb187a28b Mon Sep 17 00:00:00 2001 From: Corvin Szimion Date: Mon, 31 Mar 2025 15:55:43 +0200 Subject: [PATCH 03/58] -added "isFullscreenDialog" Parameter to all Dialog.Show methods -adjust the Popups size depending on the "isFullscreenDialog" --- src/MainDemo.Wpf/Dialogs.xaml.cs | 2 +- src/MainDemo.Wpf/MainWindow.xaml.cs | 7 +- src/MaterialDesignThemes.Wpf/DialogHost.cs | 90 ++++++++++++++++------ 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/MainDemo.Wpf/Dialogs.xaml.cs b/src/MainDemo.Wpf/Dialogs.xaml.cs index f8816bb7df..79c221572c 100644 --- a/src/MainDemo.Wpf/Dialogs.xaml.cs +++ b/src/MainDemo.Wpf/Dialogs.xaml.cs @@ -80,7 +80,7 @@ private async void Sample6_OpenDialog(object sender, RoutedEventArgs e) Message = { Text = "Some dialog content" } }; - await DialogHost.Show(sampleMessageDialog, "sampleDialog6"); + //await DialogHost.Show(sampleMessageDialog, "sampleDialog6"); } private void Sample6_ResetBlur(object sender, RoutedEventArgs e) diff --git a/src/MainDemo.Wpf/MainWindow.xaml.cs b/src/MainDemo.Wpf/MainWindow.xaml.cs index 41e24837b7..9d30feb78d 100644 --- a/src/MainDemo.Wpf/MainWindow.xaml.cs +++ b/src/MainDemo.Wpf/MainWindow.xaml.cs @@ -68,14 +68,15 @@ private void UIElement_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEven MenuToggleButton.IsChecked = false; } + int i = 0; private async void MenuPopupButton_OnClick(object sender, RoutedEventArgs e) { var sampleMessageDialog = new SampleMessageDialog { Message = { Text = ((ButtonBase)sender).Content.ToString() } }; - - await DialogHost.Show(sampleMessageDialog, "RootDialog"); + i++; + await DialogHost.Show(sampleMessageDialog, "RootDialog", (i % 2) == 0); } private void OnCopy(object sender, ExecutedRoutedEventArgs e) @@ -115,5 +116,5 @@ private static void ModifyTheme(bool isDarkTheme) private void OnSelectedItemChanged(object sender, DependencyPropertyChangedEventArgs e) => MainScrollViewer.ScrollToHome(); - + } diff --git a/src/MaterialDesignThemes.Wpf/DialogHost.cs b/src/MaterialDesignThemes.Wpf/DialogHost.cs index 2a3bd59f77..c38f6e005a 100644 --- a/src/MaterialDesignThemes.Wpf/DialogHost.cs +++ b/src/MaterialDesignThemes.Wpf/DialogHost.cs @@ -78,8 +78,8 @@ static DialogHost() /// /// Content to show (can be a control or view model). /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content) - => Show(content, null, null); + public static Task Show(object content, bool isFullscreenDialog = false) + => Show(content, null, null, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -87,8 +87,8 @@ static DialogHost() /// Content to show (can be a control or view model). /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, DialogOpenedEventHandler openedEventHandler) - => Show(content, null, openedEventHandler, null); + public static Task Show(object content, DialogOpenedEventHandler openedEventHandler, bool isFullscreenDialog = false) + => Show(content, null, openedEventHandler, null, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -96,8 +96,8 @@ static DialogHost() /// Content to show (can be a control or view model). /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, DialogClosingEventHandler closingEventHandler) - => Show(content, null, null, closingEventHandler); + public static Task Show(object content, DialogClosingEventHandler closingEventHandler, bool isFullscreenDialog = false) + => Show(content, null, null, closingEventHandler, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -106,8 +106,8 @@ static DialogHost() /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler) - => Show(content, null, openedEventHandler, closingEventHandler); + public static Task Show(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, bool isFullscreenDialog = false) + => Show(content, null, openedEventHandler, closingEventHandler, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -117,8 +117,8 @@ static DialogHost() /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Allows access to closed event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler) - => Show(content, null, openedEventHandler, closingEventHandler, closedEventHandler); + public static Task Show(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler, bool isFullscreenDialog = false) + => Show(content, null, openedEventHandler, closingEventHandler, closedEventHandler, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -126,8 +126,8 @@ static DialogHost() /// Content to show (can be a control or view model). /// of the instance where the dialog should be shown. Typically this will match an identifier set in XAML. null is allowed. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, object dialogIdentifier) - => Show(content, dialogIdentifier, null, null); + public static Task Show(object content, object dialogIdentifier, bool isFullscreenDialog = false) + => Show(content, dialogIdentifier, null, null, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -136,8 +136,8 @@ static DialogHost() /// of the instance where the dialog should be shown. Typically this will match an identifier set in XAML. null is allowed. /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, object dialogIdentifier, DialogOpenedEventHandler openedEventHandler) - => Show(content, dialogIdentifier, openedEventHandler, null); + public static Task Show(object content, object dialogIdentifier, DialogOpenedEventHandler openedEventHandler, bool isFullscreenDialog = false) + => Show(content, dialogIdentifier, openedEventHandler, null, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -146,8 +146,8 @@ static DialogHost() /// of the instance where the dialog should be shown. Typically this will match an identifier set in XAML. null is allowed. /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, object dialogIdentifier, DialogClosingEventHandler closingEventHandler) - => Show(content, dialogIdentifier, null, closingEventHandler); + public static Task Show(object content, object dialogIdentifier, DialogClosingEventHandler closingEventHandler, bool isFullscreenDialog = false) + => Show(content, dialogIdentifier, null, closingEventHandler, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -157,8 +157,8 @@ static DialogHost() /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, object? dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler) - => Show(content, dialogIdentifier, openedEventHandler, closingEventHandler, null); + public static Task Show(object content, object? dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, bool isFullscreenDialog = false) + => Show(content, dialogIdentifier, openedEventHandler, closingEventHandler, null, isFullscreenDialog); /// /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). @@ -169,10 +169,10 @@ static DialogHost() /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Allows access to closed event which would otherwise have been subscribed to on a instance. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static async Task Show(object content, object? dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler) + public static async Task Show(object content, object? dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler, bool isFullscreenDialog = false) { if (content is null) throw new ArgumentNullException(nameof(content)); - return await GetInstance(dialogIdentifier).ShowInternal(content, openedEventHandler, closingEventHandler, closedEventHandler); + return await GetInstance(dialogIdentifier).ShowInternal(content, openedEventHandler, closingEventHandler, closedEventHandler, isFullscreenDialog); } /// @@ -255,7 +255,7 @@ private static DialogHost GetInstance(object? dialogIdentifier) return targets[0]; } - internal async Task ShowInternal(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler) + internal async Task ShowInternal(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler, bool isFullscreenDialog = false) { if (IsOpen) throw new InvalidOperationException("DialogHost is already open."); @@ -264,6 +264,26 @@ private static DialogHost GetInstance(object? dialogIdentifier) AssertTargetableContent(); + if (isFullscreenDialog) + { + if (_popup is PopupEx popupEx) + { + _nonFullScreenDialogMargin = DialogMargin; + DialogMargin = new Thickness(0); + + _nonFullScreenDialogContentUniformCornerRadius = DialogContentUniformCornerRadius; + DialogContentUniformCornerRadius = 0; + RefreshPopupSize(); + popupEx.RefreshPosition(); + } + // TODO handle overwritten Dialog Templates + } + else + { + DialogMargin = _nonFullScreenDialogMargin; + DialogContentUniformCornerRadius = _nonFullScreenDialogContentUniformCornerRadius; + } + if (content != null) DialogContent = content; @@ -281,6 +301,16 @@ private static DialogHost GetInstance(object? dialogIdentifier) return result; } + private void _popup_SizeChanged(object sender, SizeChangedEventArgs e) => RefreshPopupSize(); + + private void RefreshPopupSize() + { + if (_popup is not null) + { + _popup.Height = this.ActualHeight; + _popup.Width = this.ActualWidth; + } + } #endregion public DialogHost() @@ -446,8 +476,15 @@ public object? DialogContent } public static readonly DependencyProperty DialogContentUniformCornerRadiusProperty = DependencyProperty.Register( - nameof(DialogContentUniformCornerRadius), typeof(double), typeof(DialogHost), new PropertyMetadata(4d)); + nameof(DialogContentUniformCornerRadius), typeof(double), typeof(DialogHost), new PropertyMetadata(4d, OnDialogContentUniformCornerRadiusChanged)); + + private static void OnDialogContentUniformCornerRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var obj = (DialogHost)d; + obj._nonFullScreenDialogContentUniformCornerRadius = (double)e.NewValue; + } + private double _nonFullScreenDialogContentUniformCornerRadius; public double DialogContentUniformCornerRadius { get => (double)GetValue(DialogContentUniformCornerRadiusProperty); @@ -484,6 +521,7 @@ public string? DialogContentStringFormat public static readonly DependencyProperty DialogMarginProperty = DependencyProperty.Register( "DialogMargin", typeof(Thickness), typeof(DialogHost), new PropertyMetadata(default(Thickness))); + private Thickness _nonFullScreenDialogMargin; public Thickness DialogMargin { get => (Thickness)GetValue(DialogMarginProperty); @@ -491,7 +529,13 @@ public Thickness DialogMargin } public static readonly DependencyProperty OpenDialogCommandDataContextSourceProperty = DependencyProperty.Register( - nameof(OpenDialogCommandDataContextSource), typeof(DialogHostOpenDialogCommandDataContextSource), typeof(DialogHost), new PropertyMetadata(default(DialogHostOpenDialogCommandDataContextSource))); + nameof(OpenDialogCommandDataContextSource), typeof(DialogHostOpenDialogCommandDataContextSource), typeof(DialogHost), new PropertyMetadata(default(DialogHostOpenDialogCommandDataContextSource), OnDialogMarginChanged)); + + private static void OnDialogMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var obj = (DialogHost)d; + obj._nonFullScreenDialogMargin = (Thickness)e.NewValue; + } /// /// Defines how a data context is sourced for a dialog if a From 5e386eb8b0873e8790f745c6de96c92fb6bf145a Mon Sep 17 00:00:00 2001 From: Corvin Date: Mon, 31 Mar 2025 19:48:33 +0200 Subject: [PATCH 04/58] before showing the Dialog remove the Dialogs Margin and CornerRadius if a full screen dialogis requested --- src/MainDemo.Wpf/Dialogs.xaml.cs | 2 +- src/MaterialDesignThemes.Wpf/DialogHost.cs | 63 +++++++++------------- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/MainDemo.Wpf/Dialogs.xaml.cs b/src/MainDemo.Wpf/Dialogs.xaml.cs index 79c221572c..f8816bb7df 100644 --- a/src/MainDemo.Wpf/Dialogs.xaml.cs +++ b/src/MainDemo.Wpf/Dialogs.xaml.cs @@ -80,7 +80,7 @@ private async void Sample6_OpenDialog(object sender, RoutedEventArgs e) Message = { Text = "Some dialog content" } }; - //await DialogHost.Show(sampleMessageDialog, "sampleDialog6"); + await DialogHost.Show(sampleMessageDialog, "sampleDialog6"); } private void Sample6_ResetBlur(object sender, RoutedEventArgs e) diff --git a/src/MaterialDesignThemes.Wpf/DialogHost.cs b/src/MaterialDesignThemes.Wpf/DialogHost.cs index c38f6e005a..e34ba2bb14 100644 --- a/src/MaterialDesignThemes.Wpf/DialogHost.cs +++ b/src/MaterialDesignThemes.Wpf/DialogHost.cs @@ -65,6 +65,8 @@ public class DialogHost : ContentControl private DialogClosedEventHandler? _attachedDialogClosedEventHandler; private IInputElement? _restoreFocusDialogClose; private Action? _currentSnackbarMessageQueueUnPauseAction; + private Thickness _previousDialogMargin; + private double _previousDialogContentUniformCornerRadius; static DialogHost() { @@ -264,24 +266,16 @@ private static DialogHost GetInstance(object? dialogIdentifier) AssertTargetableContent(); + // Remove the dialogs margin and corner radius if a fullscreen dialog is requested if (isFullscreenDialog) { - if (_popup is PopupEx popupEx) - { - _nonFullScreenDialogMargin = DialogMargin; - DialogMargin = new Thickness(0); + _previousDialogMargin = DialogMargin; + DialogMargin = new Thickness(0); - _nonFullScreenDialogContentUniformCornerRadius = DialogContentUniformCornerRadius; - DialogContentUniformCornerRadius = 0; - RefreshPopupSize(); - popupEx.RefreshPosition(); - } - // TODO handle overwritten Dialog Templates - } - else - { - DialogMargin = _nonFullScreenDialogMargin; - DialogContentUniformCornerRadius = _nonFullScreenDialogContentUniformCornerRadius; + _previousDialogContentUniformCornerRadius = DialogContentUniformCornerRadius; + DialogContentUniformCornerRadius = 0; + + SetPopupSize(ActualHeight, ActualWidth); } if (content != null) @@ -298,17 +292,24 @@ private static DialogHost GetInstance(object? dialogIdentifier) _asyncShowClosingEventHandler = null; _asyncShowClosedEventHandler = null; + // Set the dialogs margin and corner radius to be a non-fullscreen dialog + if (isFullscreenDialog) + { + DialogMargin = _previousDialogMargin; + DialogContentUniformCornerRadius = _previousDialogContentUniformCornerRadius; + SetPopupSize(double.NaN, double.NaN); + } return result; } - private void _popup_SizeChanged(object sender, SizeChangedEventArgs e) => RefreshPopupSize(); - - private void RefreshPopupSize() + private void SetPopupSize(double height, double width) { - if (_popup is not null) + // TODO handle overwritten "PART_Popup"s which are not of type "PopupEx" + if (_popup is PopupEx popupEx) { - _popup.Height = this.ActualHeight; - _popup.Width = this.ActualWidth; + _popup.Height = height; + _popup.Width = width; + popupEx.RefreshPosition(); } } #endregion @@ -476,15 +477,8 @@ public object? DialogContent } public static readonly DependencyProperty DialogContentUniformCornerRadiusProperty = DependencyProperty.Register( - nameof(DialogContentUniformCornerRadius), typeof(double), typeof(DialogHost), new PropertyMetadata(4d, OnDialogContentUniformCornerRadiusChanged)); - - private static void OnDialogContentUniformCornerRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var obj = (DialogHost)d; - obj._nonFullScreenDialogContentUniformCornerRadius = (double)e.NewValue; - } + nameof(DialogContentUniformCornerRadius), typeof(double), typeof(DialogHost), new PropertyMetadata(4d)); - private double _nonFullScreenDialogContentUniformCornerRadius; public double DialogContentUniformCornerRadius { get => (double)GetValue(DialogContentUniformCornerRadiusProperty); @@ -521,7 +515,6 @@ public string? DialogContentStringFormat public static readonly DependencyProperty DialogMarginProperty = DependencyProperty.Register( "DialogMargin", typeof(Thickness), typeof(DialogHost), new PropertyMetadata(default(Thickness))); - private Thickness _nonFullScreenDialogMargin; public Thickness DialogMargin { get => (Thickness)GetValue(DialogMarginProperty); @@ -529,13 +522,7 @@ public Thickness DialogMargin } public static readonly DependencyProperty OpenDialogCommandDataContextSourceProperty = DependencyProperty.Register( - nameof(OpenDialogCommandDataContextSource), typeof(DialogHostOpenDialogCommandDataContextSource), typeof(DialogHost), new PropertyMetadata(default(DialogHostOpenDialogCommandDataContextSource), OnDialogMarginChanged)); - - private static void OnDialogMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var obj = (DialogHost)d; - obj._nonFullScreenDialogMargin = (Thickness)e.NewValue; - } + nameof(OpenDialogCommandDataContextSource), typeof(DialogHostOpenDialogCommandDataContextSource), typeof(DialogHost), new PropertyMetadata(default(DialogHostOpenDialogCommandDataContextSource))); /// /// Defines how a data context is sourced for a dialog if a @@ -572,7 +559,7 @@ public object? CloseOnClickAwayParameter } public static readonly DependencyProperty SnackbarMessageQueueProperty = DependencyProperty.Register( - "SnackbarMessageQueue", typeof(SnackbarMessageQueue), typeof(DialogHost), new PropertyMetadata(default(SnackbarMessageQueue), SnackbarMessageQueuePropertyChangedCallback)); + nameof(SnackbarMessageQueue), typeof(SnackbarMessageQueue), typeof(DialogHost), new PropertyMetadata(default(SnackbarMessageQueue), SnackbarMessageQueuePropertyChangedCallback)); private static void SnackbarMessageQueuePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { From 44660753b64c938c1f02c0ca33a90ae44a9a9acf Mon Sep 17 00:00:00 2001 From: Corvin Date: Mon, 31 Mar 2025 19:52:31 +0200 Subject: [PATCH 05/58] added summary description for "isFullscreenDialog" --- src/MaterialDesignThemes.Wpf/DialogHost.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/MaterialDesignThemes.Wpf/DialogHost.cs b/src/MaterialDesignThemes.Wpf/DialogHost.cs index e34ba2bb14..0ea865a8f8 100644 --- a/src/MaterialDesignThemes.Wpf/DialogHost.cs +++ b/src/MaterialDesignThemes.Wpf/DialogHost.cs @@ -79,6 +79,7 @@ static DialogHost() /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). /// /// Content to show (can be a control or view model). + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, bool isFullscreenDialog = false) => Show(content, null, null, isFullscreenDialog); @@ -87,7 +88,8 @@ static DialogHost() /// Shows a modal dialog. To use, a instance must be in a visual tree (typically this may be specified towards the root of a Window's XAML). /// /// Content to show (can be a control or view model). - /// Allows access to opened event which would otherwise have been subscribed to on a instance. + /// Allows access to opened event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, DialogOpenedEventHandler openedEventHandler, bool isFullscreenDialog = false) => Show(content, null, openedEventHandler, null, isFullscreenDialog); @@ -97,6 +99,7 @@ static DialogHost() /// /// Content to show (can be a control or view model). /// Allows access to closing event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, DialogClosingEventHandler closingEventHandler, bool isFullscreenDialog = false) => Show(content, null, null, closingEventHandler, isFullscreenDialog); @@ -107,6 +110,7 @@ static DialogHost() /// Content to show (can be a control or view model). /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Allows access to closing event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, bool isFullscreenDialog = false) => Show(content, null, openedEventHandler, closingEventHandler, isFullscreenDialog); @@ -118,6 +122,7 @@ static DialogHost() /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Allows access to closed event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler, bool isFullscreenDialog = false) => Show(content, null, openedEventHandler, closingEventHandler, closedEventHandler, isFullscreenDialog); @@ -127,6 +132,7 @@ static DialogHost() /// /// Content to show (can be a control or view model). /// of the instance where the dialog should be shown. Typically this will match an identifier set in XAML. null is allowed. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, object dialogIdentifier, bool isFullscreenDialog = false) => Show(content, dialogIdentifier, null, null, isFullscreenDialog); @@ -137,6 +143,7 @@ static DialogHost() /// Content to show (can be a control or view model). /// of the instance where the dialog should be shown. Typically this will match an identifier set in XAML. null is allowed. /// Allows access to opened event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, object dialogIdentifier, DialogOpenedEventHandler openedEventHandler, bool isFullscreenDialog = false) => Show(content, dialogIdentifier, openedEventHandler, null, isFullscreenDialog); @@ -147,6 +154,7 @@ static DialogHost() /// Content to show (can be a control or view model). /// of the instance where the dialog should be shown. Typically this will match an identifier set in XAML. null is allowed. /// Allows access to closing event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, object dialogIdentifier, DialogClosingEventHandler closingEventHandler, bool isFullscreenDialog = false) => Show(content, dialogIdentifier, null, closingEventHandler, isFullscreenDialog); @@ -158,6 +166,7 @@ static DialogHost() /// of the instance where the dialog should be shown. Typically this will match an identifier set in XAML. null is allowed. /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Allows access to closing event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static Task Show(object content, object? dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, bool isFullscreenDialog = false) => Show(content, dialogIdentifier, openedEventHandler, closingEventHandler, null, isFullscreenDialog); @@ -170,6 +179,7 @@ static DialogHost() /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Allows access to closed event which would otherwise have been subscribed to on a instance. + /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static async Task Show(object content, object? dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler, bool isFullscreenDialog = false) { From 34f7767eb1336df419af0d31deb9c67c8fd06fd1 Mon Sep 17 00:00:00 2001 From: Corvin Date: Mon, 31 Mar 2025 20:09:06 +0200 Subject: [PATCH 06/58] applied newer "syntactic sugar" features --- src/MaterialDesignThemes.Wpf/DialogHost.cs | 52 ++++++++++++---------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/DialogHost.cs b/src/MaterialDesignThemes.Wpf/DialogHost.cs index 0ea865a8f8..4a8a1219d6 100644 --- a/src/MaterialDesignThemes.Wpf/DialogHost.cs +++ b/src/MaterialDesignThemes.Wpf/DialogHost.cs @@ -50,7 +50,7 @@ public class DialogHost : ContentControl /// public static readonly RoutedCommand CloseDialogCommand = new(); - private static readonly HashSet> LoadedInstances = new(); + private static readonly HashSet> LoadedInstances = []; private DialogOpenedEventHandler? _asyncShowOpenedEventHandler; private DialogClosingEventHandler? _asyncShowClosingEventHandler; @@ -91,7 +91,7 @@ static DialogHost() /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, DialogOpenedEventHandler openedEventHandler, bool isFullscreenDialog = false) + public static Task Show(object content, DialogOpenedEventHandler? openedEventHandler, bool isFullscreenDialog = false) => Show(content, null, openedEventHandler, null, isFullscreenDialog); /// @@ -101,7 +101,7 @@ static DialogHost() /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, DialogClosingEventHandler closingEventHandler, bool isFullscreenDialog = false) + public static Task Show(object content, DialogClosingEventHandler? closingEventHandler, bool isFullscreenDialog = false) => Show(content, null, null, closingEventHandler, isFullscreenDialog); /// @@ -145,7 +145,7 @@ static DialogHost() /// Allows access to opened event which would otherwise have been subscribed to on a instance. /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, object dialogIdentifier, DialogOpenedEventHandler openedEventHandler, bool isFullscreenDialog = false) + public static Task Show(object content, object dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, bool isFullscreenDialog = false) => Show(content, dialogIdentifier, openedEventHandler, null, isFullscreenDialog); /// @@ -156,7 +156,7 @@ static DialogHost() /// Allows access to closing event which would otherwise have been subscribed to on a instance. /// Controls whether the dialog is fullscreen or not. /// Task result is the parameter used to close the dialog, typically what is passed to the command. - public static Task Show(object content, object dialogIdentifier, DialogClosingEventHandler closingEventHandler, bool isFullscreenDialog = false) + public static Task Show(object content, object dialogIdentifier, DialogClosingEventHandler? closingEventHandler, bool isFullscreenDialog = false) => Show(content, dialogIdentifier, null, closingEventHandler, isFullscreenDialog); /// @@ -183,7 +183,11 @@ static DialogHost() /// Task result is the parameter used to close the dialog, typically what is passed to the command. public static async Task Show(object content, object? dialogIdentifier, DialogOpenedEventHandler? openedEventHandler, DialogClosingEventHandler? closingEventHandler, DialogClosedEventHandler? closedEventHandler, bool isFullscreenDialog = false) { +#if NET7_0_OR_GREATER + ArgumentNullException.ThrowIfNull(content); +#else if (content is null) throw new ArgumentNullException(nameof(content)); +#endif return await GetInstance(dialogIdentifier).ShowInternal(content, openedEventHandler, closingEventHandler, closedEventHandler, isFullscreenDialog); } @@ -233,7 +237,7 @@ private static DialogHost GetInstance(object? dialogIdentifier) if (LoadedInstances.Count == 0) throw new InvalidOperationException("No loaded DialogHost instances."); - List targets = new(); + List targets = []; foreach (var instance in LoadedInstances.ToList()) { if (instance.TryGetTarget(out DialogHost? dialogInstance)) @@ -288,7 +292,7 @@ private static DialogHost GetInstance(object? dialogIdentifier) SetPopupSize(ActualHeight, ActualWidth); } - if (content != null) + if (content is not null) DialogContent = content; _asyncShowOpenedEventHandler = openedEventHandler; @@ -352,7 +356,7 @@ private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObj { var dialogHost = (DialogHost)dependencyObject; - if (dialogHost._popupContentControl != null) + if (dialogHost._popupContentControl is not null) ValidationAssist.SetSuppress(dialogHost._popupContentControl, !dialogHost.IsOpen); VisualStateManager.GoToState(dialogHost, dialogHost.GetStateName(), !TransitionAssist.GetDisableTransitions(dialogHost)); @@ -363,7 +367,7 @@ private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObj else { dialogHost._attachedDialogClosingEventHandler = null; - if (dialogHost._currentSnackbarMessageQueueUnPauseAction != null) + if (dialogHost._currentSnackbarMessageQueueUnPauseAction is not null) { dialogHost._currentSnackbarMessageQueueUnPauseAction(); dialogHost._currentSnackbarMessageQueueUnPauseAction = null; @@ -413,7 +417,7 @@ private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObj var window = Window.GetWindow(dialogHost); if (!dialogHost.IsRestoreFocusDisabled) { - dialogHost._restoreFocusDialogClose = window != null ? FocusManager.GetFocusedElement(window) : null; + dialogHost._restoreFocusDialogClose = window is not null ? FocusManager.GetFocusedElement(window) : null; // Check restore focus override if (dialogHost._restoreFocusDialogClose is DependencyObject dependencyObj && @@ -439,7 +443,7 @@ private static void IsOpenPropertyChangedCallback(DependencyObject dependencyObj CommandManager.InvalidateRequerySuggested(); UIElement? child = dialogHost.FocusPopup(); - if (child != null) + if (child is not null) { //https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/187 //totally not happy about this, but on immediate validation we can get some weird looking stuff...give WPF a kick to refresh... @@ -523,7 +527,7 @@ public string? DialogContentStringFormat } public static readonly DependencyProperty DialogMarginProperty = DependencyProperty.Register( - "DialogMargin", typeof(Thickness), typeof(DialogHost), new PropertyMetadata(default(Thickness))); + nameof(DialogMargin), typeof(Thickness), typeof(DialogHost), new PropertyMetadata(default(Thickness))); public Thickness DialogMargin { @@ -545,7 +549,7 @@ public DialogHostOpenDialogCommandDataContextSource OpenDialogCommandDataContext } public static readonly DependencyProperty CloseOnClickAwayProperty = DependencyProperty.Register( - "CloseOnClickAway", typeof(bool), typeof(DialogHost), new PropertyMetadata(default(bool))); + nameof(CloseOnClickAway), typeof(bool), typeof(DialogHost), new PropertyMetadata(default(bool))); /// /// Indicates whether the dialog will close if the user clicks off the dialog, on the obscured background. @@ -557,7 +561,7 @@ public bool CloseOnClickAway } public static readonly DependencyProperty CloseOnClickAwayParameterProperty = DependencyProperty.Register( - "CloseOnClickAwayParameter", typeof(object), typeof(DialogHost), new PropertyMetadata(default(object))); + nameof(CloseOnClickAwayParameter), typeof(object), typeof(DialogHost), new PropertyMetadata(default(object))); /// /// Parameter to provide to close handlers if an close due to click away is instigated. @@ -574,7 +578,7 @@ public object? CloseOnClickAwayParameter private static void SnackbarMessageQueuePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { var dialogHost = (DialogHost)dependencyObject; - if (dialogHost._currentSnackbarMessageQueueUnPauseAction != null) + if (dialogHost._currentSnackbarMessageQueueUnPauseAction is not null) { dialogHost._currentSnackbarMessageQueueUnPauseAction(); dialogHost._currentSnackbarMessageQueueUnPauseAction = null; @@ -659,14 +663,14 @@ public double BlurRadius public override void OnApplyTemplate() { - if (_contentCoverGrid != null) + if (_contentCoverGrid is not null) _contentCoverGrid.MouseLeftButtonUp -= ContentCoverGridOnMouseLeftButtonUp; _popup = GetTemplateChild(PopupPartName) as Popup; _popupContentControl = GetTemplateChild(PopupContentPartName) as ContentControl; _contentCoverGrid = GetTemplateChild(ContentCoverGridName) as Grid; - if (_contentCoverGrid != null) + if (_contentCoverGrid is not null) _contentCoverGrid.MouseLeftButtonUp += ContentCoverGridOnMouseLeftButtonUp; VisualStateManager.GoToState(this, GetStateName(), false); @@ -700,7 +704,7 @@ public bool IsRestoreFocusDisabled public static readonly RoutedEvent DialogOpenedEvent = EventManager.RegisterRoutedEvent( - "DialogOpened", + nameof(DialogOpened), RoutingStrategy.Bubble, typeof(DialogOpenedEventHandler), typeof(DialogHost)); @@ -747,7 +751,7 @@ protected void OnDialogOpened(DialogOpenedEventArgs eventArgs) public static readonly RoutedEvent DialogClosingEvent = EventManager.RegisterRoutedEvent( - "DialogClosing", + nameof(DialogClosing), RoutingStrategy.Bubble, typeof(DialogClosingEventHandler), typeof(DialogHost)); @@ -790,7 +794,7 @@ protected void OnDialogClosing(DialogClosingEventArgs eventArgs) public static readonly RoutedEvent DialogClosedEvent = EventManager.RegisterRoutedEvent( - "DialogClosed", + nameof(DialogClosed), RoutingStrategy.Bubble, typeof(DialogClosedEventHandler), typeof(DialogHost)); @@ -836,7 +840,7 @@ protected void OnDialogClosed(DialogClosedEventArgs eventArgs) internal void AssertTargetableContent() { var existingBinding = BindingOperations.GetBindingExpression(this, DialogContentProperty); - if (existingBinding != null) + if (existingBinding is not null) throw new InvalidOperationException( "Content cannot be passed to a dialog via the OpenDialog if DialogContent already has a binding."); } @@ -927,7 +931,7 @@ protected override void OnPreviewMouseDown(MouseButtonEventArgs e) private void ContentCoverGridOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs) { - if (CloseOnClickAway && CurrentSession != null) + if (CloseOnClickAway && CurrentSession is not null) InternalClose(CloseOnClickAwayParameter); } @@ -942,11 +946,11 @@ private void OpenDialogHandler(object sender, ExecutedRoutedEventArgs executedRo _attachedDialogClosedEventHandler = GetDialogClosedAttached(dependencyObject); } - if (executedRoutedEventArgs.Parameter != null) + if (executedRoutedEventArgs.Parameter is not null) { AssertTargetableContent(); - if (_popupContentControl != null) + if (_popupContentControl is not null) { _popupContentControl.DataContext = OpenDialogCommandDataContextSource switch { From 485a4ca070da36cb536d016efaf6c2c856398c65 Mon Sep 17 00:00:00 2001 From: Corvin Szimion Date: Tue, 1 Apr 2025 12:34:51 +0200 Subject: [PATCH 07/58] resize and reposition the PopupEx if the Window is resized or repositioned --- src/MainDemo.Wpf/MainWindow.xaml | 3 +++ src/MaterialDesignThemes.Wpf/DialogHost.cs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/MainDemo.Wpf/MainWindow.xaml b/src/MainDemo.Wpf/MainWindow.xaml index d88dbb49c5..80097d3132 100644 --- a/src/MainDemo.Wpf/MainWindow.xaml +++ b/src/MainDemo.Wpf/MainWindow.xaml @@ -127,6 +127,9 @@ Foreground="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}" Style="{StaticResource MaterialDesignToolButton}" ToolTip="Home" /> + + - @@ -1129,7 +1127,7 @@ - + @@ -1217,5 +1215,4 @@ - diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml index c31d7f292a..87a1323164 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml @@ -178,7 +178,7 @@ - - - diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Snackbar.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Snackbar.xaml index 97c582c8f8..657831e36b 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Snackbar.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Snackbar.xaml @@ -88,18 +88,18 @@ ContentStringFormat="{TemplateBinding ActionContentStringFormat}" ContentTemplate="{TemplateBinding ActionContentTemplate}" ContentTemplateSelector="{TemplateBinding ActionContentTemplateSelector}" - Style="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type wpf:Snackbar}}, Path=ActionButtonStyle}" + Style="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type wpf:Snackbar}}, Path=ActionButtonStyle, FallbackValue={x:Null}}" Visibility="{TemplateBinding ActionContent, Converter={x:Static converters:NullableToVisibilityConverter.CollapsedInstance}}"> - - - + + + - @@ -223,5 +223,4 @@ - diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TabControl.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TabControl.xaml index b97a2f48d4..87867f8aee 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TabControl.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TabControl.xaml @@ -23,7 +23,7 @@ @@ -486,7 +486,7 @@ @@ -557,7 +557,7 @@ - + @@ -594,7 +594,6 @@ - @@ -669,7 +668,6 @@ Visibility="Collapsed" /> - - + - diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TimePicker.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TimePicker.xaml index cb2d9de90c..5f09d6a8ef 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TimePicker.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TimePicker.xaml @@ -35,7 +35,7 @@ - + @@ -202,5 +202,4 @@ - diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml index 66b5d16d89..a683767fc3 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml @@ -5,7 +5,7 @@ - + - diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeListView.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeListView.xaml index c2f8a0afa4..0f13b84415 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeListView.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeListView.xaml @@ -98,8 +98,8 @@ - - + + @@ -107,7 +107,7 @@ - + @@ -234,7 +234,7 @@ - + diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeView.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeView.xaml index 644d3a6f18..9b5cb01956 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeView.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.TreeView.xaml @@ -165,8 +165,8 @@ - - + + @@ -379,7 +379,7 @@ - + From 6cef2f5e905fba681eb70e87e4b0722c54bdec17 Mon Sep 17 00:00:00 2001 From: Nicolai Henriksen Date: Fri, 6 Jun 2025 06:19:32 +0200 Subject: [PATCH 29/58] Forward HintAssist.HintPaddingBrush into nested TextBox of UpDown (#3857) --- .../MaterialDesignTheme.NumericUpDown.xaml | 87 ++++++++++--------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.NumericUpDown.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.NumericUpDown.xaml index 710267e00a..5df54ef2aa 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.NumericUpDown.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.NumericUpDown.xaml @@ -58,49 +58,50 @@ + Padding="{TemplateBinding Padding, Converter={StaticResource NumericUpDownPaddingConverter}}" + VerticalAlignment="Stretch" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" + internal:InternalTextFieldAssist.IsMouseOver="{TemplateBinding IsMouseOver}" + wpf:HintAssist.Background="{TemplateBinding wpf:HintAssist.Background}" + wpf:HintAssist.FloatingOffset="{TemplateBinding wpf:HintAssist.FloatingOffset}" + wpf:HintAssist.FloatingScale="{TemplateBinding wpf:HintAssist.FloatingScale}" + wpf:HintAssist.FontFamily="{TemplateBinding wpf:HintAssist.FontFamily}" + wpf:HintAssist.Foreground="{TemplateBinding wpf:HintAssist.Foreground}" + wpf:HintAssist.HelperTextStyle="{TemplateBinding wpf:HintAssist.HelperTextStyle}" + wpf:HintAssist.Hint="{TemplateBinding wpf:HintAssist.Hint}" + wpf:HintAssist.HintOpacity="{TemplateBinding wpf:HintAssist.HintOpacity}" + wpf:HintAssist.HintPaddingBrush="{TemplateBinding wpf:HintAssist.HintPaddingBrush}" + wpf:HintAssist.IsFloating="{TemplateBinding wpf:HintAssist.IsFloating}" + wpf:TextFieldAssist.DecorationVisibility="{TemplateBinding wpf:TextFieldAssist.DecorationVisibility}" + wpf:TextFieldAssist.HasClearButton="{TemplateBinding wpf:TextFieldAssist.HasClearButton}" + wpf:TextFieldAssist.HasFilledTextField="{TemplateBinding wpf:TextFieldAssist.HasFilledTextField}" + wpf:TextFieldAssist.HasLeadingIcon="{TemplateBinding wpf:TextFieldAssist.HasLeadingIcon}" + wpf:TextFieldAssist.HasOutlinedTextField="{TemplateBinding wpf:TextFieldAssist.HasOutlinedTextField}" + wpf:TextFieldAssist.HasTrailingIcon="{TemplateBinding wpf:TextFieldAssist.HasTrailingIcon}" + wpf:TextFieldAssist.IconVerticalAlignment="{TemplateBinding wpf:TextFieldAssist.IconVerticalAlignment}" + wpf:TextFieldAssist.LeadingIcon="{TemplateBinding wpf:TextFieldAssist.LeadingIcon}" + wpf:TextFieldAssist.LeadingIconSize="{TemplateBinding wpf:TextFieldAssist.LeadingIconSize}" + wpf:TextFieldAssist.NewSpecHighlightingEnabled="{TemplateBinding wpf:TextFieldAssist.NewSpecHighlightingEnabled}" + wpf:TextFieldAssist.OutlinedBorderActiveThickness="{TemplateBinding wpf:TextFieldAssist.OutlinedBorderActiveThickness}" + wpf:TextFieldAssist.PrefixText="{TemplateBinding wpf:TextFieldAssist.PrefixText}" + wpf:TextFieldAssist.PrefixTextHintBehavior="{TemplateBinding wpf:TextFieldAssist.PrefixTextHintBehavior}" + wpf:TextFieldAssist.PrefixTextVisibility="{TemplateBinding wpf:TextFieldAssist.PrefixTextVisibility}" + wpf:TextFieldAssist.RippleOnFocusEnabled="{TemplateBinding wpf:TextFieldAssist.RippleOnFocusEnabled}" + wpf:TextFieldAssist.SuffixText="{TemplateBinding wpf:TextFieldAssist.SuffixText}" + wpf:TextFieldAssist.SuffixTextHintBehavior="{TemplateBinding wpf:TextFieldAssist.SuffixTextHintBehavior}" + wpf:TextFieldAssist.SuffixTextVisibility="{TemplateBinding wpf:TextFieldAssist.SuffixTextVisibility}" + wpf:TextFieldAssist.TextBoxViewMargin="{TemplateBinding wpf:TextFieldAssist.TextBoxViewMargin}" + wpf:TextFieldAssist.TextBoxViewVerticalAlignment="{TemplateBinding wpf:TextFieldAssist.TextBoxViewVerticalAlignment}" + wpf:TextFieldAssist.TextFieldCornerRadius="{TemplateBinding wpf:TextFieldAssist.TextFieldCornerRadius}" + wpf:TextFieldAssist.TrailingIcon="{TemplateBinding wpf:TextFieldAssist.TrailingIcon}" + wpf:TextFieldAssist.TrailingIconSize="{TemplateBinding wpf:TextFieldAssist.TrailingIconSize}" + wpf:TextFieldAssist.UnderlineBrush="{TemplateBinding wpf:TextFieldAssist.UnderlineBrush}" + wpf:TextFieldAssist.UnderlineCornerRadius="{TemplateBinding wpf:TextFieldAssist.UnderlineCornerRadius}" + BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" + Focusable="{TemplateBinding Focusable}" + Style="{DynamicResource NestedTextBoxStyle}" /> Date: Sun, 8 Jun 2025 07:32:31 +0200 Subject: [PATCH 30/58] Modify DecimalUpDown basic test to ensure culture is respected in string-value conversions (#3865) * Consume new XAMLTest and ensure tests execute * Data-drive the (basic) DecimalUpDown test to verify culture-differences * Add net9.0 in the pipeline workflow --- .github/workflows/build_artifacts.yml | 1 + Directory.packages.props | 2 +- global.json | 2 +- .../MaterialDesignThemes.UITests.csproj | 2 +- .../WPF/UpDownControls/DecimalUpDownTests.cs | 26 ++++++++++++++----- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 36ffa01414..7cdd62e2e2 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -34,6 +34,7 @@ jobs: dotnet-version: | 6.x 8.x + 9.x - name: Restore dependencies run: dotnet restore ${{ env.solution }} diff --git a/Directory.packages.props b/Directory.packages.props index b58226de08..8fa27d2bfd 100644 --- a/Directory.packages.props +++ b/Directory.packages.props @@ -26,7 +26,7 @@ - + diff --git a/global.json b/global.json index c19a2e057c..ccbe73ca13 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "9.0.300", "rollForward": "latestMinor" } } diff --git a/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj b/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj index c75b048ec9..2612cc5af3 100644 --- a/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj +++ b/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj @@ -1,6 +1,6 @@  - net8.0-windows + net9.0-windows false false true diff --git a/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/DecimalUpDownTests.cs b/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/DecimalUpDownTests.cs index c65ff7fb8f..6cced0c83e 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/DecimalUpDownTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/DecimalUpDownTests.cs @@ -1,17 +1,26 @@ -using System.ComponentModel; +using System.ComponentModel; +using System.Globalization; +using System.Threading; namespace MaterialDesignThemes.UITests.WPF.UpDownControls; public class DecimalUpDownTests(ITestOutputHelper output) : TestBase(output) { - [Fact] - public async Task NumericButtons_IncreaseAndDecreaseValue() + [Theory] + [InlineData("en-US")] + [InlineData("da-DK")] + [InlineData("fa-IR")] + [InlineData("ja-JP")] + [InlineData("zh-CN")] + public async Task NumericButtons_IncreaseAndDecreaseValue(string culture) { await using var recorder = new TestRecorder(App); var numericUpDown = await LoadXaml(""" - + """); + await App.RemoteExecute(SetCulture, [culture]); + var plusButton = await numericUpDown.GetElement("PART_IncreaseButton"); var minusButton = await numericUpDown.GetElement("PART_DecreaseButton"); var textBox = await numericUpDown.GetElement("PART_TextBox"); @@ -22,8 +31,8 @@ public async Task NumericButtons_IncreaseAndDecreaseValue() await plusButton.LeftClick(); await Wait.For(async () => { - Assert.Equal("2", await textBox.GetText()); - Assert.Equal(2, await numericUpDown.GetValue()); + Assert.Equal(Convert.ToString(1.1, CultureInfo.GetCultureInfo(culture)), await textBox.GetText()); + Assert.Equal((decimal)1.1, await numericUpDown.GetValue()); }); await minusButton.LeftClick(); @@ -34,6 +43,11 @@ await Wait.For(async () => }); recorder.Success(); + + static void SetCulture(Application _, string culture) + { + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture); + } } [Fact] From f0f5fdcb8fc437dbca636476fe6208fc86ce44dd Mon Sep 17 00:00:00 2001 From: Joris Voermans Date: Sun, 8 Jun 2025 07:48:53 +0200 Subject: [PATCH 31/58] Fixed PopUpBox VerticalContentAlignment (#3864) --- .../Themes/MaterialDesignTheme.PopupBox.xaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml index 87a1323164..31a116ee5e 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml @@ -80,6 +80,7 @@ + From 442f311a2f49325c451f87bebb256c7202defd2b Mon Sep 17 00:00:00 2001 From: Joris Voermans Date: Fri, 13 Jun 2025 06:06:31 +0200 Subject: [PATCH 32/58] Used x:Reference in Button and ProgressBar ControlTemplates (#3868) --- .../Themes/MaterialDesignTheme.Button.xaml | 56 +++++++++---------- .../MaterialDesignTheme.ProgressBar.xaml | 18 +++--- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Button.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Button.xaml index 3dca4cb4e2..a0cd5a8cde 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Button.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Button.xaml @@ -68,10 +68,10 @@ Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ButtonProgressAssist.Value)}"> - - - - + + + + @@ -87,10 +87,10 @@ SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> - - - - + + + + @@ -199,10 +199,10 @@ Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ButtonProgressAssist.Value)}"> - - - - + + + + @@ -217,10 +217,10 @@ SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> - - - - + + + + @@ -357,10 +357,10 @@ Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ButtonProgressAssist.Value)}"> - - - - + + + + @@ -375,10 +375,10 @@ SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> - - - - + + + + @@ -802,10 +802,10 @@ SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> - - - - + + + + diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ProgressBar.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ProgressBar.xaml index 4ad30d2d68..7fbe3d00a9 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ProgressBar.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ProgressBar.xaml @@ -184,26 +184,26 @@ StrokeThickness="3"> - - + + - + - + - + - + - + @@ -212,7 +212,9 @@ - + From 182b60a7c18255b8f1cfeca4c5674a96e56d59cf Mon Sep 17 00:00:00 2001 From: Joris Voermans Date: Fri, 13 Jun 2025 06:21:33 +0200 Subject: [PATCH 33/58] Chip: Fixed without icon padding (#3869) --- .../Themes/MaterialDesignTheme.Chip.xaml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Chip.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Chip.xaml index 162a95909d..3f510f055b 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Chip.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Chip.xaml @@ -28,10 +28,10 @@ - + - + @@ -63,7 +63,7 @@ + FontSize="11" /> @@ -161,14 +161,14 @@ - - - - - + + + + + From 556b412cede978a3ff3a141c2176478f44466709 Mon Sep 17 00:00:00 2001 From: Joris Voermans Date: Fri, 13 Jun 2025 06:24:42 +0200 Subject: [PATCH 34/58] ToggleButton uses EllipseClipConverter (#3867) * ToggleButton uses EllipseClipConverter * EllipseClipConverter: Changed to file-scoped namespace --- .../Converters/EllipseClipConverter.cs | 40 +++++++++++++++++++ .../MaterialDesignTheme.ToggleButton.xaml | 36 ++++------------- 2 files changed, 48 insertions(+), 28 deletions(-) create mode 100644 src/MaterialDesignThemes.Wpf/Converters/EllipseClipConverter.cs diff --git a/src/MaterialDesignThemes.Wpf/Converters/EllipseClipConverter.cs b/src/MaterialDesignThemes.Wpf/Converters/EllipseClipConverter.cs new file mode 100644 index 0000000000..d5dbb3b3f0 --- /dev/null +++ b/src/MaterialDesignThemes.Wpf/Converters/EllipseClipConverter.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Windows.Media; + +namespace MaterialDesignThemes.Wpf.Converters; + +public class EllipseClipConverter : IMultiValueConverter +{ + public static readonly EllipseClipConverter Instance = new(); + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values is [double width, double height, ..]) + { + if (width < 1.0 || height < 1.0) + { + return Geometry.Empty; + } + + Point center = new Point(width / 2.0, height / 2.0); + double radiusX = width / 2.0; + double radiusY = height / 2.0; + + EllipseGeometry geometry = new EllipseGeometry(center, radiusX, radiusY); + geometry.Freeze(); + + return geometry; + } + + return DependencyProperty.UnsetValue; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + => throw new NotImplementedException(); +} diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml index a683767fc3..52e85b15ca 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.ToggleButton.xaml @@ -136,20 +136,10 @@ ContentTemplate="{TemplateBinding ContentTemplate}" FlowDirection="LeftToRight" /> - - - - - - - - + + + + @@ -164,20 +154,10 @@ ContentTemplate="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ToggleButtonAssist.OnContentTemplate)}" FlowDirection="LeftToRight" /> - - - - - - - - + + + + From cf1afbc518af30aa886be84667587b57da9220d9 Mon Sep 17 00:00:00 2001 From: L1nstead <134846129+L1nstead@users.noreply.github.com> Date: Fri, 13 Jun 2025 07:43:21 +0100 Subject: [PATCH 35/58] Remove .NET 6 as EOL (#3859) --- .github/workflows/build_artifacts.yml | 1 - MaterialDesignToolkit.Full.sln | 3 --- .../MahAppsDragablzDemo.csproj | 2 +- src/MainDemo.Wpf/MaterialDesignDemo.csproj | 2 +- .../MaterialDesign3Demo.csproj | 2 +- .../MaterialDesignColors.Wpf.csproj | 2 +- .../MaterialDesignColors.nuspec | 4 +--- .../MaterialDesignDemo.Shared.csproj | 2 +- .../MaterialDesignThemes.MahApps.csproj | 2 +- .../MaterialDesignThemes.MahApps.nuspec | 9 +-------- .../MaterialDesignThemes.Wpf.csproj | 2 +- .../MaterialDesignThemes.nuspec | 10 +--------- .../MaterialDesignColors.Wpf.Tests.csproj | 2 +- .../MaterialDesignThemes.Wpf.Tests.csproj | 2 +- 14 files changed, 12 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 7cdd62e2e2..851278a9dc 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -32,7 +32,6 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.x 8.x 9.x diff --git a/MaterialDesignToolkit.Full.sln b/MaterialDesignToolkit.Full.sln index d27338256c..6414dbdcc2 100644 --- a/MaterialDesignToolkit.Full.sln +++ b/MaterialDesignToolkit.Full.sln @@ -33,9 +33,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.packages.props = Directory.packages.props .config\dotnet-tools.json = .config\dotnet-tools.json global.json = global.json - MaterialDesignColors.nuspec = MaterialDesignColors.nuspec - MaterialDesignThemes.MahApps.nuspec = MaterialDesignThemes.MahApps.nuspec - MaterialDesignThemes.nuspec = MaterialDesignThemes.nuspec build\MigrateBrushes.ps1 = build\MigrateBrushes.ps1 nuget.config = nuget.config README.md = README.md diff --git a/src/MahMaterialDragablzMashUp/MahAppsDragablzDemo.csproj b/src/MahMaterialDragablzMashUp/MahAppsDragablzDemo.csproj index 4a38274b38..e434e02469 100644 --- a/src/MahMaterialDragablzMashUp/MahAppsDragablzDemo.csproj +++ b/src/MahMaterialDragablzMashUp/MahAppsDragablzDemo.csproj @@ -2,7 +2,7 @@ WinExe - net472;net6.0-windows;net8.0-windows + net472;net8.0-windows true ..\ true diff --git a/src/MainDemo.Wpf/MaterialDesignDemo.csproj b/src/MainDemo.Wpf/MaterialDesignDemo.csproj index c461bb71dc..311ef14627 100644 --- a/src/MainDemo.Wpf/MaterialDesignDemo.csproj +++ b/src/MainDemo.Wpf/MaterialDesignDemo.csproj @@ -2,7 +2,7 @@ WinExe - net472;net6.0-windows;net8.0-windows + net472;net8.0-windows true true favicon.ico diff --git a/src/MaterialDesign3.Demo.Wpf/MaterialDesign3Demo.csproj b/src/MaterialDesign3.Demo.Wpf/MaterialDesign3Demo.csproj index 28830a00ba..319b1a9bb7 100644 --- a/src/MaterialDesign3.Demo.Wpf/MaterialDesign3Demo.csproj +++ b/src/MaterialDesign3.Demo.Wpf/MaterialDesign3Demo.csproj @@ -2,7 +2,7 @@ WinExe - net472;net6.0-windows;net8.0-windows + net472;net8.0-windows true true favicon.ico diff --git a/src/MaterialDesignColors.Wpf/MaterialDesignColors.Wpf.csproj b/src/MaterialDesignColors.Wpf/MaterialDesignColors.Wpf.csproj index f92936d0a5..ed1a0c31e9 100644 --- a/src/MaterialDesignColors.Wpf/MaterialDesignColors.Wpf.csproj +++ b/src/MaterialDesignColors.Wpf/MaterialDesignColors.Wpf.csproj @@ -2,7 +2,7 @@ MaterialDesignColors MaterialDesignColors - net462;net6.0-windows;net8.0-windows + net462;net8.0-windows true 1.0.1 $([System.Text.RegularExpressions.Regex]::Replace("$(MDIXColorsVersion)", "-ci\d+$", "")) diff --git a/src/MaterialDesignColors.Wpf/MaterialDesignColors.nuspec b/src/MaterialDesignColors.Wpf/MaterialDesignColors.nuspec index a309b13841..008e852af7 100644 --- a/src/MaterialDesignColors.Wpf/MaterialDesignColors.nuspec +++ b/src/MaterialDesignColors.Wpf/MaterialDesignColors.nuspec @@ -26,9 +26,7 @@ - - \ No newline at end of file + diff --git a/src/MaterialDesignDemo.Shared/MaterialDesignDemo.Shared.csproj b/src/MaterialDesignDemo.Shared/MaterialDesignDemo.Shared.csproj index c1d486b0c3..65688d2960 100644 --- a/src/MaterialDesignDemo.Shared/MaterialDesignDemo.Shared.csproj +++ b/src/MaterialDesignDemo.Shared/MaterialDesignDemo.Shared.csproj @@ -1,7 +1,7 @@ - net472;net6.0-windows;net8.0-windows + net472;net8.0-windows true enable diff --git a/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.csproj b/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.csproj index 9f4fe5504f..5d0eb7f639 100644 --- a/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.csproj +++ b/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.csproj @@ -1,7 +1,7 @@  - net462;net6.0-windows;net8.0-windows + net462;net8.0-windows true 1.0.1 $([System.Text.RegularExpressions.Regex]::Replace("$(MDIXMahAppsVersion)", "-ci\d+$", "")) diff --git a/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.nuspec b/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.nuspec index 064f3ec7a5..bdd981bb4a 100644 --- a/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.nuspec +++ b/src/MaterialDesignThemes.MahApps/MaterialDesignThemes.MahApps.nuspec @@ -22,11 +22,6 @@ - - - - - @@ -38,9 +33,7 @@ - - \ No newline at end of file + diff --git a/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj b/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj index 262be69548..592420a96b 100644 --- a/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj +++ b/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.Wpf.csproj @@ -1,7 +1,7 @@ - net462;net6.0-windows;net8.0-windows + net462;net8.0-windows true 1.0.1 $([System.Text.RegularExpressions.Regex]::Replace("$(MDIXVersion)", "-ci\d+$", "")) diff --git a/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.nuspec b/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.nuspec index 3472b6c5dd..6dac8e637c 100644 --- a/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.nuspec +++ b/src/MaterialDesignThemes.Wpf/MaterialDesignThemes.nuspec @@ -21,10 +21,6 @@ - - - - @@ -35,14 +31,10 @@ - - - \ No newline at end of file + diff --git a/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj b/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj index 66ee05538b..f2ab67fc5e 100644 --- a/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj +++ b/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj @@ -1,7 +1,7 @@  - net472;net6.0-windows;net8.0-windows + net472;net8.0-windows MaterialDesignColors.Wpf.Tests MaterialDesignColors.Wpf.Tests diff --git a/tests/MaterialDesignThemes.Wpf.Tests/MaterialDesignThemes.Wpf.Tests.csproj b/tests/MaterialDesignThemes.Wpf.Tests/MaterialDesignThemes.Wpf.Tests.csproj index 65558c0973..788b56e8af 100644 --- a/tests/MaterialDesignThemes.Wpf.Tests/MaterialDesignThemes.Wpf.Tests.csproj +++ b/tests/MaterialDesignThemes.Wpf.Tests/MaterialDesignThemes.Wpf.Tests.csproj @@ -1,7 +1,7 @@  - net472;net6.0-windows;net8.0-windows + net472;net8.0-windows MaterialDesignThemes.Wpf.Tests MaterialDesignThemes.Wpf.Tests From 66b836ba4e7f64b7ab02ea7642ae5544ad59e38f Mon Sep 17 00:00:00 2001 From: Joris Voermans Date: Fri, 13 Jun 2025 20:18:11 +0200 Subject: [PATCH 36/58] TextBoxLineCountBehavior: Fixed NullReferenceException when used within Virtualizing DataGrid (#3871) --- .../Behaviors/TextBoxLineCountBehavior.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs b/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs index 6952856690..5da4a5613b 100644 --- a/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs +++ b/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs @@ -1,4 +1,5 @@ -using Microsoft.Xaml.Behaviors; +using System.Windows.Threading; +using Microsoft.Xaml.Behaviors; namespace MaterialDesignThemes.Wpf.Behaviors; @@ -14,8 +15,13 @@ private void UpdateAttachedProperties() { if (AssociatedObject is { } associatedObject) { - associatedObject.SetCurrentValue(TextFieldAssist.TextBoxLineCountProperty, associatedObject.LineCount); - associatedObject.SetCurrentValue(TextFieldAssist.TextBoxIsMultiLineProperty, associatedObject.LineCount > 1); + associatedObject.Dispatcher + .BeginInvoke(() => + { + associatedObject.SetCurrentValue(TextFieldAssist.TextBoxLineCountProperty, associatedObject.LineCount); + associatedObject.SetCurrentValue(TextFieldAssist.TextBoxIsMultiLineProperty, associatedObject.LineCount > 1); + }, + DispatcherPriority.Background); } } From 4733a54434d5b0984cf630a538d88e41297ce27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Ehrenm=C3=BCller?= Date: Wed, 18 Jun 2025 19:25:56 +0200 Subject: [PATCH 37/58] Evaluate TextBox.LineCount only once in UpdateAttachedProperties (#3874) --- .../Behaviors/TextBoxLineCountBehavior.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs b/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs index 5da4a5613b..2df10c85a3 100644 --- a/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs +++ b/src/MaterialDesignThemes.Wpf/Behaviors/TextBoxLineCountBehavior.cs @@ -18,8 +18,9 @@ private void UpdateAttachedProperties() associatedObject.Dispatcher .BeginInvoke(() => { - associatedObject.SetCurrentValue(TextFieldAssist.TextBoxLineCountProperty, associatedObject.LineCount); - associatedObject.SetCurrentValue(TextFieldAssist.TextBoxIsMultiLineProperty, associatedObject.LineCount > 1); + int lineCount = associatedObject.LineCount; + associatedObject.SetCurrentValue(TextFieldAssist.TextBoxLineCountProperty, lineCount); + associatedObject.SetCurrentValue(TextFieldAssist.TextBoxIsMultiLineProperty, lineCount > 1); }, DispatcherPriority.Background); } From 122a5345fbaf8231c8dbdb6203c765922492d779 Mon Sep 17 00:00:00 2001 From: Corvin <43533385+corvinsz@users.noreply.github.com> Date: Thu, 19 Jun 2025 06:42:56 +0200 Subject: [PATCH 38/58] added ToolTip demo page with a RichToolTip example. Applied new syntax in demo apps. Expose PopupAnimation in PopupBox (#3872) --- .../Domain/MainWindowViewModel.cs | 8 + src/MainDemo.Wpf/ToolTips.xaml | 167 ++++++++++++++++ src/MainDemo.Wpf/ToolTips.xaml.cs | 15 ++ .../Domain/MainWindowViewModel.cs | 180 ++++++++---------- src/MaterialDesign3.Demo.Wpf/ToolTips.xaml | 167 ++++++++++++++++ src/MaterialDesign3.Demo.Wpf/ToolTips.xaml.cs | 13 ++ .../Domain/ToolTipsViewModel.cs | 62 ++++++ src/MaterialDesignThemes.Wpf/PopupBox.cs | 19 +- .../Themes/MaterialDesignTheme.PopupBox.xaml | 5 +- 9 files changed, 530 insertions(+), 106 deletions(-) create mode 100644 src/MainDemo.Wpf/ToolTips.xaml create mode 100644 src/MainDemo.Wpf/ToolTips.xaml.cs create mode 100644 src/MaterialDesign3.Demo.Wpf/ToolTips.xaml create mode 100644 src/MaterialDesign3.Demo.Wpf/ToolTips.xaml.cs create mode 100644 src/MaterialDesignDemo.Shared/Domain/ToolTipsViewModel.cs diff --git a/src/MainDemo.Wpf/Domain/MainWindowViewModel.cs b/src/MainDemo.Wpf/Domain/MainWindowViewModel.cs index 12e4238fc8..feb1fef7cf 100644 --- a/src/MainDemo.Wpf/Domain/MainWindowViewModel.cs +++ b/src/MainDemo.Wpf/Domain/MainWindowViewModel.cs @@ -450,6 +450,14 @@ private static IEnumerable GenerateDemoItems(ISnackbarMessageQueue sna DocumentationLink.ApiLink(), DocumentationLink.ApiLink() ]); + + yield return new DemoItem( + "ToolTips", + typeof(ToolTips), + [ + DocumentationLink.DemoPageLink(), + DocumentationLink.DemoPageLink("Demo View Model", "Domain"), + ]); } private bool DemoItemsFilter(object obj) diff --git a/src/MainDemo.Wpf/ToolTips.xaml b/src/MainDemo.Wpf/ToolTips.xaml new file mode 100644 index 0000000000..df0cbf315b --- /dev/null +++ b/src/MainDemo.Wpf/ToolTips.xaml @@ -0,0 +1,167 @@ + + + + + + + + 0,16,0,0 + + + + + + + + + public Elevation PopupElevation { - get { return (Elevation) GetValue(PopupElevationProperty); } - set { SetValue(PopupElevationProperty, value); } + get => (Elevation)GetValue(PopupElevationProperty); + set => SetValue(PopupElevationProperty, value); + } + + /// + /// Gets or sets the animation of the popup card. + /// + public static readonly DependencyProperty PopupAnimationProperty = DependencyProperty.Register( + nameof(PopupAnimation), typeof(PopupAnimation), typeof(PopupBox), new PropertyMetadata(PopupAnimation.Fade)); + + /// + /// Gets or sets the animation of the popup card. + /// + public PopupAnimation PopupAnimation + { + get => (PopupAnimation)GetValue(PopupAnimationProperty); + set => SetValue(PopupAnimationProperty, value); } /// diff --git a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml index 31a116ee5e..e65875261b 100644 --- a/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml +++ b/src/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.PopupBox.xaml @@ -128,7 +128,7 @@ HorizontalOffset="{TemplateBinding PopupElevation, Converter={StaticResource ElevationRadiusConverter}}" IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsPopupOpen, Mode=TwoWay}" Placement="Custom" - PopupAnimation="Fade" + PopupAnimation="{TemplateBinding PopupAnimation}" VerticalOffset="{TemplateBinding PopupElevation, Converter={StaticResource ElevationRadiusConverter}}"> @@ -200,6 +200,7 @@ + @@ -404,7 +405,7 @@ CustomPopupPlacementCallback="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PopupPlacementMethod}" IsOpen="False" Placement="Custom" - PopupAnimation="None"> + PopupAnimation="{TemplateBinding PopupAnimation}"> From bbdee061c02c44539ad143bd0522a08bcd7548ab Mon Sep 17 00:00:00 2001 From: MohammadHadi Attarieh Date: Thu, 26 Jun 2025 11:42:03 +0330 Subject: [PATCH 39/58] fix when the TreeListView expands too much content, scrolling to the top will shrink the content #3640 (#3875) Related Work Items: #3 Co-authored-by: MohammadHadi Attarieh --- src/MaterialDesignThemes.Wpf/TreeListView.cs | 44 +++++++++++-------- .../TreeListViewItem.cs | 13 +++++- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/TreeListView.cs b/src/MaterialDesignThemes.Wpf/TreeListView.cs index e5c778773c..8f907f73b9 100644 --- a/src/MaterialDesignThemes.Wpf/TreeListView.cs +++ b/src/MaterialDesignThemes.Wpf/TreeListView.cs @@ -51,24 +51,24 @@ protected override DependencyObject GetContainerForItemOverride() protected override bool IsItemItsOwnContainerOverride(object? item) => item is TreeListViewItem; - protected override void PrepareContainerForItemOverride(DependencyObject element, object? item) - { - base.PrepareContainerForItemOverride(element, item); + //protected override void PrepareContainerForItemOverride(DependencyObject element, object? item) + //{ + // base.PrepareContainerForItemOverride(element, item); - if (element is TreeListViewItem treeListViewItem) - { - int level = 0; - bool isExpanded = false; - int index = ItemContainerGenerator.IndexFromContainer(treeListViewItem); - if (index >= 0 && InternalItemsSource is { } itemsSource) - { - level = itemsSource.GetLevel(index); - isExpanded = itemsSource.GetIsExpanded(index); - } + // if (element is TreeListViewItem treeListViewItem) + // { + // int level = 0; + // bool isExpanded = false; + // int index = ItemContainerGenerator.IndexFromContainer(treeListViewItem); + // if (index >= 0 && InternalItemsSource is { } itemsSource) + // { + // level = itemsSource.GetLevel(index); + // isExpanded = itemsSource.GetIsExpanded(index); + // } - treeListViewItem.PrepareTreeListViewItem(item, this, level, isExpanded); - } - } + // treeListViewItem.PrepareTreeListViewItem(item, this, level, isExpanded); + // } + //} protected override void ClearContainerForItemOverride(DependencyObject element, object item) { @@ -86,11 +86,19 @@ internal void ItemExpandedChanged(TreeListViewItem item) int index = ItemContainerGenerator.IndexFromContainer(item); //Issue 3572 if (index < 0) return; - var children = item.GetChildren().ToList(); - bool isExpanded = item.IsExpanded; + var isExpanded = item.IsExpanded; + var internalIsExpanded = itemsSource.GetIsExpanded(index); + + if (internalIsExpanded == isExpanded) + { + return; + } + itemsSource.SetIsExpanded(index, isExpanded); + if (isExpanded) { + var children = item.GetChildren().ToList(); int parentLevel = itemsSource.GetLevel(index); for (int i = 0; i < children.Count; i++) { diff --git a/src/MaterialDesignThemes.Wpf/TreeListViewItem.cs b/src/MaterialDesignThemes.Wpf/TreeListViewItem.cs index 8aeab1aee8..3c42c8285b 100644 --- a/src/MaterialDesignThemes.Wpf/TreeListViewItem.cs +++ b/src/MaterialDesignThemes.Wpf/TreeListViewItem.cs @@ -146,7 +146,18 @@ public override void OnApplyTemplate() void OnTemplateChanged(object? sender, EventArgs e) { - PrepareTreeListViewItem(Content, TreeListView!, Level, IsExpanded); + int level = 0; + bool isExpanded = false; + int index = TreeListView!.ItemContainerGenerator.IndexFromContainer(this); + + + if (index >= 0 && TreeListView.InternalItemsSource is { } itemsSource) + { + level = itemsSource.GetLevel(index); + isExpanded = itemsSource.GetIsExpanded(index); + } + + PrepareTreeListViewItem(Content, TreeListView, level, isExpanded); } } } From f31fe7812312c4829fccc23844f6c3cfd2c4a0c3 Mon Sep 17 00:00:00 2001 From: Kevin B Date: Thu, 26 Jun 2025 23:04:19 -0700 Subject: [PATCH 40/58] Updating tests to use TUnit and MTP (#3838) * Updating to xUnit v3 1.x * Adding MTP https://devblogs.microsoft.com/dotnet/mtp-adoption-frameworks/ * Starting conversion to TUnit * Tests all compile * Adds cancellation tokens to async delays Ensures that asynchronous delay calls use cancellation tokens to allow for tests to be cancelled correctly. * WIP * Refactors test suite and updates dependencies Updates the test suite to use TUnit for improved test clarity and maintainability. Removes unused xunit packages and addresses nullable warnings in RatingBar converters. Also includes changes to CustomColorTheme, DataGridAssist, DialogHost, DrawerHost, FlipperAssist, Internal/TreeListViewItemsCollection, Label, MaterialDesignThemes.Wpf.Tests.csproj, MdixHelper, PackIcon, PopupBox, RatingBar, SnackbarMessageQueue, TextBlock, TextBox, Theme and Transitioner tests. * Updates test framework and fixes assertions Updates test framework from Fact to Test. Updates tests to use Assert.That for improved readability and consistency. * Reverting stupid * Save point * Assert.True * Updates tests to be async Updates tests to be async to prevent possible deadlocks and improve reliability. * Removes TUnit dependencies from tests Removes the TUnit library and associated code from test projects, modernizing testing using the default testing framework. * Tests are now compiling * Updates test framework for DecimalUpDown Converts the DecimalUpDown test to use the `Arguments` attribute and updates assertions to improve reliability and clarity. * Fixing member data sources Updates TimePicker unit tests to use MethodDataSource with Func to prevent test data from being eagerly evaluated, resolving issues with the test framework. * Refactors code for brevity and adds tests Updates collection initialization to use more concise syntax. Adds tests for the ButtonProgressAssist, CheckBoxAssist, ColorPicker, and DataGridAssist classes. Adds a new test executor and removes redundant STAThreadExecutor attributes. Adds NotInParallel attribute to CalendarFormatInfoTests and DialogHostTests. Refactors the PreviewIndicatorTransformXConverter and PreviewIndicatorTransformYConverter. * Updates screenshot artifact path Updates the path for screenshot artifacts to target the .NET 9 framework. * Fixes artifact upload path Corrects the artifact upload path in the build workflow. * Updates coordinate assertion method Uses IsCloseTo method for asserting coordinate proximity in UI tests for ComboBoxes, DatePickers, PasswordBoxes, and TextBoxes. This improves the accuracy and reliability of the tests by checking if the coordinates are within a specified tolerance range. * Updates NuGet package versions Updates several NuGet package versions, including Microsoft.NET.Test.Sdk, TUnit, and XAMLTest. Fixes a potential issue in a test case by using CancellationToken.None instead of TestContext.Current!.CancellationToken * Fixes UI test failures Corrects failing UI tests by adjusting assertions and adding exception handling. The fixes improve the stability and reliability of the UI test suite. * Fixes combo box test assertion Updates test assertion to expect a null text value when a combo box is uninitialized. This corrects the expected behavior of the test. * Updates artifact path for .NET 9 Updates the artifact upload path in the build workflow to correctly target the .NET 9 framework. This resolves an issue where artifacts were not being correctly uploaded due to an incorrect target framework name. * Adds success recorder in auto suggest box tests * Skips flaky GetDialogSession test * Skips failing saturation drag test Skips a ColorPicker test that was failing before the move to Machine.Time.Past due to timing issues in the while loop. * Ensures DialogHost is properly unloaded Introduces a `ControlHost` class to ensure the `DialogHost` is properly unloaded when the test is complete. This prevents orphaned `DialogHost` instances from interfering with subsequent tests. --- .github/workflows/build_artifacts.yml | 2 +- Directory.packages.props | 13 +- .../SwatchesProvider.cs | 18 +- src/MaterialDesignThemes.Wpf/DialogHost.cs | 4 +- .../Internal/TreeListViewItemsCollection.cs | 4 +- src/MaterialDesignThemes.Wpf/RatingBar.cs | 38 +- .../ResourceDictionaryExtensions.g.cs | 2 +- src/MaterialDesignThemes.Wpf/Theme.g.cs | 2 +- .../ThemeExtensions.g.cs | 2 +- .../Brushes.cs | 2 +- .../ColorAssistTests.cs | 9 +- .../MaterialDesignColors.Wpf.Tests.csproj | 11 +- .../ResourceProviderFixture.cs | 41 +- .../MaterialDesignThemes.UITests/AllStyles.cs | 63 ++- .../MaterialDesignSpec.cs | 7 +- .../MaterialDesignThemes.UITests.csproj | 12 +- .../Validation/ValidationUpdates.xaml.cs | 4 +- .../TUnit/IsCloseToExtensions.cs | 51 ++ .../MaterialDesignThemes.UITests/TestBase.cs | 21 +- .../AutoSuggestTextBoxTests.cs | 76 +-- .../WPF/Buttons/OutlineButtonTests.cs | 20 +- .../WPF/Buttons/RaisedButtonTests.cs | 8 +- .../WPF/Cards/ElevatedCardTests.cs | 17 +- .../WPF/Cards/OutlinedCardTests.cs | 18 +- .../WPF/ColorPickerTests.cs | 11 +- .../WPF/ColorZones/ColorZoneTests.cs | 33 +- .../WPF/ComboBoxes/ComboBoxTests.cs | 113 +++-- .../ContentControls/ContentControlTests.cs | 13 +- .../WPF/DatePickers/DatePickerTests.cs | 120 +++-- .../WPF/DialogHosts/DialogHostTests.cs | 160 ++++--- .../WPF/DrawerHosts/DrawerHostTests.cs | 55 +-- .../WPF/Flippers/ClassicFlipperTests.cs | 24 +- .../WPF/ListBoxes/ListBoxTests.cs | 71 ++- .../WPF/PasswordBoxes/PasswordBoxTests.cs | 167 +++---- .../WPF/PopupBoxes/PopupBoxTests.cs | 20 +- .../WPF/SnackBars/SnackBarTests.cs | 11 +- .../WPF/SplitButtons/SplitButtonTests.cs | 47 +- .../WPF/TabControls/TabControlTests.cs | 49 +- .../WPF/TextBoxes/TextBoxTests.cs | 163 +++---- .../WPF/TextFieldDefaultHeightTests.cs | 53 +- .../WPF/Theme/ColorAdjustTests.cs | 21 +- .../WPF/Theme/ThemeTests.cs | 51 +- .../WPF/Theme/ThemeTests.g.cs | 292 +++++------ .../TimePickers/MaterialDesignTimePicker.cs | 6 +- .../WPF/TimePickers/TimePickerTests.cs | 228 +++++---- .../WPF/ToolBars/ToolBarTests.cs | 14 +- .../WPF/TreeListViews/TreeListViewTests.cs | 161 +++---- .../WPF/TreeViews/TreeViewTests.cs | 35 +- .../WPF/UpDownControls/DecimalUpDownTests.cs | 97 ++-- .../WPF/UpDownControls/NumericUpDownTests.cs | 101 ++-- .../XamlTestExtensions.cs | 2 +- .../AdornerExtensionsTests.cs | 17 +- .../BundledThemeTests.cs | 36 +- .../ButtonProgressAssistTests.cs | 111 ++--- .../CalendarFormatInfoTests.cs | 130 ++--- .../CheckBoxAssistTests.cs | 24 +- .../ClockTests.cs | 48 +- .../ColorPickerTests.cs | 165 ++++--- .../Converters/BrushOpacityConverterTests.cs | 14 +- .../DoubleToCornerRadiusConverterTests.cs | 24 +- .../Converters/MathConverterTests.cs | 19 +- .../Converters/MathMultipleConverterTests.cs | 11 +- .../Converters/SliderToolTipConverterTests.cs | 28 +- .../CustomColorThemeTests.cs | 41 +- .../DataGridAssistTests.cs | 115 ++--- .../DialogHostTests.cs | 452 ++++++++++-------- .../DrawerHostTests.cs | 48 +- .../EnumDataAttribute.cs | 30 +- .../FlipperAssistTests.cs | 49 +- .../TreeListViewItemsCollectionTests.cs | 341 ++++++------- .../LabelTests.cs | 14 +- .../MaterialDesignThemes.Wpf.Tests.csproj | 35 +- .../MdixHelper.cs | 8 +- .../PackIconTests.cs | 8 +- .../PopupBoxTests.cs | 20 +- .../Properties/AssemblyInfo.cs | 4 +- .../RatingBarTests.cs | 297 ++++++------ .../SnackbarMessageQueueItemTests.cs | 36 +- .../SnackbarMessageQueueTests.cs | 77 ++- .../TextBlockTests.cs | 9 +- .../TextBoxTests.cs | 14 +- .../ThemeTests.cs | 18 +- .../TimePickerUnitTests.cs | 217 ++++----- .../TransitionerTests.cs | 28 +- .../TreeViewTests.cs | 8 +- 85 files changed, 2520 insertions(+), 2539 deletions(-) create mode 100644 tests/MaterialDesignThemes.UITests/TUnit/IsCloseToExtensions.cs diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 851278a9dc..389c02a410 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -54,7 +54,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: Screenshots-${{ github.run_number }} - path: ${{ github.workspace }}tests\MaterialDesignThemes.UITests\bin\${{ inputs.build-configuration }}\net8.0-windows7\Screenshots + path: ${{ github.workspace }}\tests\MaterialDesignThemes.UITests\bin\${{ inputs.build-configuration }}\net9.0-windows\Screenshots if-no-files-found: ignore - name: Build NuGets diff --git a/Directory.packages.props b/Directory.packages.props index 8fa27d2bfd..37ca871f37 100644 --- a/Directory.packages.props +++ b/Directory.packages.props @@ -1,9 +1,9 @@ - + - + @@ -14,7 +14,7 @@ - + @@ -24,11 +24,10 @@ + + - - - - + diff --git a/src/MaterialDesignColors.Wpf/SwatchesProvider.cs b/src/MaterialDesignColors.Wpf/SwatchesProvider.cs index 81385d83bb..3caefa06bc 100644 --- a/src/MaterialDesignColors.Wpf/SwatchesProvider.cs +++ b/src/MaterialDesignColors.Wpf/SwatchesProvider.cs @@ -12,6 +12,7 @@ namespace MaterialDesignColors; /// public class SwatchesProvider { + private static readonly object _syncLock = new(); /// /// Generates an instance reading swatches from the provided assembly, allowing /// colours outside of the standard material palette to be loaded provided the are stored in the expected XAML format. @@ -39,12 +40,7 @@ public SwatchesProvider(Assembly assembly) Read(assemblyName, x.SingleOrDefault(y => y.match.Groups["type"].Value == "primary")?.key), Read(assemblyName, x.SingleOrDefault(y => y.match.Groups["type"].Value == "secondary")?.key) )) - .ToList() ?? -#if NETCOREAPP3_1_OR_GREATER - (IEnumerable)Array.Empty(); -#else - (IEnumerable)new Swatch[0]; -#endif + .ToList() ?? []; } /// @@ -98,8 +94,12 @@ static Hue GetHue(ResourceDictionary dictionary, DictionaryEntry entry) if (assemblyName is null || path is null) return null; - return (ResourceDictionary)Application.LoadComponent(new Uri( - $"/{assemblyName};component/{path.Replace(".baml", ".xaml")}", - UriKind.RelativeOrAbsolute)); + lock (_syncLock) + { + //NB: Application.LoadComponent is not thread safe + return (ResourceDictionary)Application.LoadComponent(new Uri( + $"/{assemblyName};component/{path.Replace(".baml", ".xaml")}", + UriKind.RelativeOrAbsolute)); + } } } diff --git a/src/MaterialDesignThemes.Wpf/DialogHost.cs b/src/MaterialDesignThemes.Wpf/DialogHost.cs index 2a3bd59f77..a1b31e2e9c 100644 --- a/src/MaterialDesignThemes.Wpf/DialogHost.cs +++ b/src/MaterialDesignThemes.Wpf/DialogHost.cs @@ -50,7 +50,7 @@ public class DialogHost : ContentControl /// public static readonly RoutedCommand CloseDialogCommand = new(); - private static readonly HashSet> LoadedInstances = new(); + private static readonly HashSet> LoadedInstances = []; private DialogOpenedEventHandler? _asyncShowOpenedEventHandler; private DialogClosingEventHandler? _asyncShowClosingEventHandler; @@ -221,7 +221,7 @@ private static DialogHost GetInstance(object? dialogIdentifier) if (LoadedInstances.Count == 0) throw new InvalidOperationException("No loaded DialogHost instances."); - List targets = new(); + List targets = []; foreach (var instance in LoadedInstances.ToList()) { if (instance.TryGetTarget(out DialogHost? dialogInstance)) diff --git a/src/MaterialDesignThemes.Wpf/Internal/TreeListViewItemsCollection.cs b/src/MaterialDesignThemes.Wpf/Internal/TreeListViewItemsCollection.cs index 324e1c1b69..0405bf0498 100644 --- a/src/MaterialDesignThemes.Wpf/Internal/TreeListViewItemsCollection.cs +++ b/src/MaterialDesignThemes.Wpf/Internal/TreeListViewItemsCollection.cs @@ -6,8 +6,8 @@ namespace MaterialDesignThemes.Wpf.Internal; public class TreeListViewItemsCollection : ObservableCollection { - private List ItemLevels { get; } = new(); - private List ItemIsExpanded { get; } = new(); + private List ItemLevels { get; } = []; + private List ItemIsExpanded { get; } = []; public TreeListViewItemsCollection(object? wrappedSource) { diff --git a/src/MaterialDesignThemes.Wpf/RatingBar.cs b/src/MaterialDesignThemes.Wpf/RatingBar.cs index 2f1b43c3ac..fd45af708b 100644 --- a/src/MaterialDesignThemes.Wpf/RatingBar.cs +++ b/src/MaterialDesignThemes.Wpf/RatingBar.cs @@ -378,7 +378,7 @@ internal class TextBlockForegroundConverter : IMultiValueConverter public static TextBlockForegroundConverter Instance { get; } = new(); - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object?[]? values, Type targetType, object? parameter, CultureInfo culture) { if (values?.Length == 5 && values[0] is SolidColorBrush brush @@ -411,28 +411,28 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur return new SolidColorBrush(semiTransparent); } - GradientStopCollection CreateGradientStopCollection(Color originalColor, Color semiTransparent, double offset, bool invertDirection) + static GradientStopCollection CreateGradientStopCollection(Color originalColor, Color semiTransparent, double offset, bool invertDirection) { if (invertDirection) { - return new() - { - new GradientStop {Color = semiTransparent, Offset = offset}, - new GradientStop {Color = originalColor, Offset = offset}, - }; - } - return new() - { + return + [ + new GradientStop {Color = semiTransparent, Offset = offset}, new GradientStop {Color = originalColor, Offset = offset}, - new GradientStop {Color = semiTransparent, Offset = offset} - }; + ]; + } + return + [ + new GradientStop {Color = originalColor, Offset = offset}, + new GradientStop {Color = semiTransparent, Offset = offset} + ]; } // This should never happen (returning actual brush to avoid the compilers squiggly line warning) return Brushes.Transparent; } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); + public object?[]? ConvertBack(object? value, Type[] targetTypes, object? parameter, CultureInfo culture) => throw new NotImplementedException(); } internal class PreviewIndicatorTransformXConverter : IMultiValueConverter @@ -441,9 +441,9 @@ internal class PreviewIndicatorTransformXConverter : IMultiValueConverter internal static double Margin => 2.0; - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object?[]? values, Type targetType, object? parameter, CultureInfo culture) { - if (values.Length >= 7 + if (values?.Length >= 7 && values[0] is double ratingBarButtonActualWidth && values[1] is double previewValueActualWidth && values[2] is Orientation ratingBarOrientation @@ -482,7 +482,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur return 1.0; } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); + public object?[]? ConvertBack(object? value, Type[] targetTypes, object? parameter, CultureInfo culture) => throw new NotImplementedException(); } internal class PreviewIndicatorTransformYConverter : IMultiValueConverter @@ -491,9 +491,9 @@ internal class PreviewIndicatorTransformYConverter : IMultiValueConverter internal static double Margin => 2.0; - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object?[]? values, Type targetType, object? parameter, CultureInfo culture) { - if (values.Length >= 7 + if (values?.Length >= 7 && values[0] is double ratingBarButtonActualHeight && values[1] is double previewValueActualHeight && values[2] is Orientation ratingBarOrientation @@ -531,6 +531,6 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur return 1.0; } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); + public object?[]? ConvertBack(object? value, Type[] targetTypes, object? parameter, CultureInfo culture) => throw new NotImplementedException(); } } diff --git a/src/MaterialDesignThemes.Wpf/ResourceDictionaryExtensions.g.cs b/src/MaterialDesignThemes.Wpf/ResourceDictionaryExtensions.g.cs index 0bbf247c4b..e80cc45778 100644 --- a/src/MaterialDesignThemes.Wpf/ResourceDictionaryExtensions.g.cs +++ b/src/MaterialDesignThemes.Wpf/ResourceDictionaryExtensions.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by mdresgen. +// This code was generated by MaterialDesignToolkit.ResourceGeneration. // //------------------------------------------------------------------------------ diff --git a/src/MaterialDesignThemes.Wpf/Theme.g.cs b/src/MaterialDesignThemes.Wpf/Theme.g.cs index 77720d50fe..d6cdffd3a6 100644 --- a/src/MaterialDesignThemes.Wpf/Theme.g.cs +++ b/src/MaterialDesignThemes.Wpf/Theme.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by mdresgen. +// This code was generated by MaterialDesignToolkit.ResourceGeneration. // //------------------------------------------------------------------------------ diff --git a/src/MaterialDesignThemes.Wpf/ThemeExtensions.g.cs b/src/MaterialDesignThemes.Wpf/ThemeExtensions.g.cs index 366f190e7e..a304456e89 100644 --- a/src/MaterialDesignThemes.Wpf/ThemeExtensions.g.cs +++ b/src/MaterialDesignThemes.Wpf/ThemeExtensions.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by mdresgen. +// This code was generated by MaterialDesignToolkit.ResourceGeneration. // //------------------------------------------------------------------------------ diff --git a/src/MaterialDesignToolkit.ResourceGeneration/Brushes.cs b/src/MaterialDesignToolkit.ResourceGeneration/Brushes.cs index ca1354b481..212aadb789 100644 --- a/src/MaterialDesignToolkit.ResourceGeneration/Brushes.cs +++ b/src/MaterialDesignToolkit.ResourceGeneration/Brushes.cs @@ -482,7 +482,7 @@ void WriteBrush(Brush brush, string name) {{indent}}{{indent}}{ {{indent}}{{indent}}{{indent}}IVisualElement textBlock = await panel.GetElement("[Text=\"{{name}}\"]"); {{indent}}{{indent}}{{indent}}Color? textBlockBackground = await textBlock.GetBackgroundColor(); - {{indent}}{{indent}}{{indent}}Assert.Equal(await GetResourceColor("{{brush.Name}}"), textBlockBackground); + {{indent}}{{indent}}{{indent}}await Assert.That(textBlockBackground).IsEqualTo(await GetResourceColor("{{brush.Name}}")); {{indent}}{{indent}}} """); } diff --git a/tests/MaterialDesignColors.Wpf.Tests/ColorAssistTests.cs b/tests/MaterialDesignColors.Wpf.Tests/ColorAssistTests.cs index 47b0bf905a..919c47035a 100644 --- a/tests/MaterialDesignColors.Wpf.Tests/ColorAssistTests.cs +++ b/tests/MaterialDesignColors.Wpf.Tests/ColorAssistTests.cs @@ -1,13 +1,12 @@ using System.Windows.Media; using MaterialDesignColors.ColorManipulation; -using Xunit; namespace MaterialDesignColors.Wpf.Tests; public class ColorAssistTests { - [Fact] - public void EnsureContrastRatio_AdjustsColor() + [Test] + public async Task EnsureContrastRatio_AdjustsColor() { var background = Color.FromRgb(0xFA, 0xFA, 0xFA); var foreground = Color.FromRgb(0xFF, 0xC1, 0x07); @@ -15,7 +14,7 @@ public void EnsureContrastRatio_AdjustsColor() var adjusted = foreground.EnsureContrastRatio(background, 3.0f); double contrastRatio = adjusted.ContrastRatio(background); - Assert.True(contrastRatio >= 2.9); - Assert.True(contrastRatio <= 3.1); + await Assert.That(contrastRatio).IsGreaterThanOrEqualTo(2.9); + await Assert.That(contrastRatio).IsLessThanOrEqualTo(3.1); } } diff --git a/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj b/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj index f2ab67fc5e..079e511042 100644 --- a/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj +++ b/tests/MaterialDesignColors.Wpf.Tests/MaterialDesignColors.Wpf.Tests.csproj @@ -4,6 +4,9 @@ net472;net8.0-windows MaterialDesignColors.Wpf.Tests MaterialDesignColors.Wpf.Tests + true + Exe + true @@ -25,9 +28,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + \ No newline at end of file diff --git a/tests/MaterialDesignColors.Wpf.Tests/ResourceProviderFixture.cs b/tests/MaterialDesignColors.Wpf.Tests/ResourceProviderFixture.cs index d54a25183a..4f3eeac307 100644 --- a/tests/MaterialDesignColors.Wpf.Tests/ResourceProviderFixture.cs +++ b/tests/MaterialDesignColors.Wpf.Tests/ResourceProviderFixture.cs @@ -1,65 +1,62 @@ -using Shouldly; -using Xunit; - -namespace MaterialDesignColors.Wpf.Fixture; +namespace MaterialDesignColors.Wpf.Tests; public class ResourceProviderFixture { - [Fact] - public void ExcludesBlack() + [Test] + public async Task ExcludesBlack() { SwatchesProvider swatchesProvider = new (); bool containsBlack = swatchesProvider.Swatches.Any( swatch => string.Compare(swatch.Name, "Black", StringComparison.InvariantCultureIgnoreCase) == 0); - containsBlack.ShouldBe(false); + await Assert.That(containsBlack).IsFalse(); } - [Fact] - public void IncludesGrey() + [Test] + public async Task IncludesGrey() { SwatchesProvider swatchesProvider = new (); bool containsBlack = swatchesProvider.Swatches.Any( swatch => string.Compare(swatch.Name, "Grey", StringComparison.InvariantCultureIgnoreCase) == 0); - containsBlack.ShouldBe(true); + await Assert.That(containsBlack).IsTrue(); } - [Fact] - public void BrownHasNoSecondary() + [Test] + public async Task BrownHasNoSecondary() { SwatchesProvider swatchesProvider = new (); var brownSwatch = swatchesProvider.Swatches.Single( swatch => swatch.Name == "brown"); - brownSwatch.SecondaryHues.ShouldNotBeNull(); - brownSwatch.SecondaryHues.Count.ShouldBe(0); + await Assert.That(brownSwatch.SecondaryHues).IsNotNull(); + await Assert.That(brownSwatch.SecondaryHues.Count).IsEqualTo(0); } - [Fact] - public void BrownHasPrimaries() + [Test] + public async Task BrownHasPrimaries() { SwatchesProvider swatchesProvider = new (); var brownSwatch = swatchesProvider.Swatches.Single( swatch => swatch.Name == "brown"); - brownSwatch.PrimaryHues.ShouldNotBeNull(); - brownSwatch.PrimaryHues.Count.ShouldBe(10); + await Assert.That(brownSwatch.PrimaryHues).IsNotNull(); + await Assert.That(brownSwatch.PrimaryHues.Count).IsEqualTo(10); } - [Fact] - public void IndigoHasSecondaries() + [Test] + public async Task IndigoHasSecondaries() { SwatchesProvider swatchesProvider = new (); var brownSwatch = swatchesProvider.Swatches.Single( swatch => swatch.Name == "indigo"); - brownSwatch.SecondaryHues.ShouldNotBeNull(); - brownSwatch.SecondaryHues.Count.ShouldBe(4); + await Assert.That(brownSwatch.SecondaryHues).IsNotNull(); + await Assert.That(brownSwatch.SecondaryHues.Count).IsEqualTo(4); } } diff --git a/tests/MaterialDesignThemes.UITests/AllStyles.cs b/tests/MaterialDesignThemes.UITests/AllStyles.cs index 2710e4b0fe..5d3c6c0f01 100644 --- a/tests/MaterialDesignThemes.UITests/AllStyles.cs +++ b/tests/MaterialDesignThemes.UITests/AllStyles.cs @@ -1,40 +1,35 @@ -using System.Reflection; -using MaterialDesignColors; +using MaterialDesignColors; namespace MaterialDesignThemes.UITests; public class AllStyles : TestBase { - public AllStyles(ITestOutputHelper output) - : base(output) - { } - - [Theory] - [InlineData("Button", "MaterialDesignRaisedButton")] - [InlineData("Calendar", "MaterialDesignCalendarPortrait")] - [InlineData("CheckBox", "MaterialDesignCheckBox")] - [InlineData("ComboBox", "MaterialDesignComboBox")] - [InlineData("DataGrid", "MaterialDesignDataGrid")] - [InlineData("DatePicker", "MaterialDesignDatePicker")] - [InlineData("Expander", "MaterialDesignExpander")] - [InlineData("GridSplitter", "MaterialDesignGridSplitter")] - [InlineData("GroupBox", "MaterialDesignGroupBox")] - [InlineData("Label", "MaterialDesignLabel")] - [InlineData("ListBox", "MaterialDesignListBox")] - [InlineData("ListView", "MaterialDesignListView")] - [InlineData("Menu", "MaterialDesignMenu")] - [InlineData("PasswordBox", "MaterialDesignPasswordBox")] - [InlineData("ProgressBar", "MaterialDesignLinearProgressBar")] - [InlineData("RadioButton", "MaterialDesignRadioButton")] - [InlineData("RichTextBox", "MaterialDesignRichTextBox")] - [InlineData("ScrollBar", "MaterialDesignScrollBar")] - [InlineData("ScrollViewer", "MaterialDesignScrollViewer")] - [InlineData("Slider", "MaterialDesignSlider")] - [InlineData("TabControl", "MaterialDesignTabControl")] - [InlineData("TextBox", "MaterialDesignTextBox")] - [InlineData("ToggleButton", "MaterialDesignSwitchToggleButton")] - [InlineData("ToolBar", "MaterialDesignToolBar")] - [InlineData("TreeView", "MaterialDesignTreeView")] + [Test] + [Arguments("Button", "MaterialDesignRaisedButton")] + [Arguments("Calendar", "MaterialDesignCalendarPortrait")] + [Arguments("CheckBox", "MaterialDesignCheckBox")] + [Arguments("ComboBox", "MaterialDesignComboBox")] + [Arguments("DataGrid", "MaterialDesignDataGrid")] + [Arguments("DatePicker", "MaterialDesignDatePicker")] + [Arguments("Expander", "MaterialDesignExpander")] + [Arguments("GridSplitter", "MaterialDesignGridSplitter")] + [Arguments("GroupBox", "MaterialDesignGroupBox")] + [Arguments("Label", "MaterialDesignLabel")] + [Arguments("ListBox", "MaterialDesignListBox")] + [Arguments("ListView", "MaterialDesignListView")] + [Arguments("Menu", "MaterialDesignMenu")] + [Arguments("PasswordBox", "MaterialDesignPasswordBox")] + [Arguments("ProgressBar", "MaterialDesignLinearProgressBar")] + [Arguments("RadioButton", "MaterialDesignRadioButton")] + [Arguments("RichTextBox", "MaterialDesignRichTextBox")] + [Arguments("ScrollBar", "MaterialDesignScrollBar")] + [Arguments("ScrollViewer", "MaterialDesignScrollViewer")] + [Arguments("Slider", "MaterialDesignSlider")] + [Arguments("TabControl", "MaterialDesignTabControl")] + [Arguments("TextBox", "MaterialDesignTextBox")] + [Arguments("ToggleButton", "MaterialDesignSwitchToggleButton")] + [Arguments("ToolBar", "MaterialDesignToolBar")] + [Arguments("TreeView", "MaterialDesignTreeView")] public async Task LoadStyleInIsolation_CanBeLoaded(string controlName, string styleName) { await using var recorder = new TestRecorder(App); @@ -57,7 +52,7 @@ public async Task LoadStyleInIsolation_CanBeLoaded(string controlName, string st await App.Initialize(applicationResourceXaml, Path.GetFullPath("MaterialDesignColors.dll"), Path.GetFullPath("MaterialDesignThemes.Wpf.dll"), - Assembly.GetExecutingAssembly().Location); + System.Reflection.Assembly.GetExecutingAssembly().Location); IWindow window = await App.CreateWindow($$""" """); - Assert.True(await window.GetIsVisible()); + await Assert.That(await window.GetIsVisible()).IsTrue(); recorder.Success(); } diff --git a/tests/MaterialDesignThemes.UITests/MaterialDesignSpec.cs b/tests/MaterialDesignThemes.UITests/MaterialDesignSpec.cs index 7247b4d8a6..4de16f29ee 100644 --- a/tests/MaterialDesignThemes.UITests/MaterialDesignSpec.cs +++ b/tests/MaterialDesignThemes.UITests/MaterialDesignSpec.cs @@ -17,11 +17,12 @@ public static class MaterialDesignSpec /// public const double MinimumContrastLargeText = 3.0; - public static void AssertContrastRatio(Color foreground, Color background, double minimumContrastRatio) + public static async Task AssertContrastRatio(Color foreground, Color background, double minimumContrastRatio) { const double tolerance = 0.1; - var ratio = ColorAssist.ContrastRatio(foreground, background); - Assert.True(ratio >= minimumContrastRatio - tolerance, $"Contrast ratio '{ratio}' is less than {minimumContrastRatio} with a tolerance of 0.1"); + double ratio = ColorAssist.ContrastRatio(foreground, background); + await Assert.That(ratio).IsGreaterThanOrEqualTo(minimumContrastRatio - tolerance) + .Because($"Contrast ratio '{ratio}' is less than {minimumContrastRatio} with a tolerance of 0.1"); } } diff --git a/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj b/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj index 2612cc5af3..8e78308ec4 100644 --- a/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj +++ b/tests/MaterialDesignThemes.UITests/MaterialDesignThemes.UITests.csproj @@ -6,6 +6,9 @@ true $(NoWarn);CA1707 true + true + Exe + true @@ -19,18 +22,13 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + - - diff --git a/tests/MaterialDesignThemes.UITests/Samples/Validation/ValidationUpdates.xaml.cs b/tests/MaterialDesignThemes.UITests/Samples/Validation/ValidationUpdates.xaml.cs index 719ead86b2..6d830021a8 100644 --- a/tests/MaterialDesignThemes.UITests/Samples/Validation/ValidationUpdates.xaml.cs +++ b/tests/MaterialDesignThemes.UITests/Samples/Validation/ValidationUpdates.xaml.cs @@ -1,6 +1,6 @@ using System.Collections; using System.ComponentModel; -using System.ComponentModel.DataAnnotations; +using System.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -46,7 +46,7 @@ private async Task CauseErrors() { Error = "Some error"; ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Text))); - await Task.Delay(100); + await Task.Delay(100, CancellationToken.None); Error += " + more"; ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Text))); } diff --git a/tests/MaterialDesignThemes.UITests/TUnit/IsCloseToExtensions.cs b/tests/MaterialDesignThemes.UITests/TUnit/IsCloseToExtensions.cs new file mode 100644 index 0000000000..1ae6934500 --- /dev/null +++ b/tests/MaterialDesignThemes.UITests/TUnit/IsCloseToExtensions.cs @@ -0,0 +1,51 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using TUnit.Assertions.AssertConditions; +using TUnit.Assertions.AssertConditions.Interfaces; +using TUnit.Assertions.AssertionBuilders; + +namespace MaterialDesignThemes.Tests.TUnit; + +public static class IsCloseToExtensions +{ + public static IsCloseToWrapper IsCloseTo(this IValueSource valueSource, double expected, double precision, [CallerArgumentExpression(nameof(expected))] string? doNotPopulateThisValue1 = null, [CallerArgumentExpression(nameof(precision))] string? doNotPopulateThisValue2 = null) + { + var assertionBuilder = valueSource.RegisterAssertion(new IsCloseToCondition(expected, precision) + , [doNotPopulateThisValue1, doNotPopulateThisValue2]); + + return new IsCloseToWrapper(assertionBuilder); + } + + public static IsCloseToWrapper IsCloseTo(this IValueSource valueSource, float expected, float precision, [CallerArgumentExpression(nameof(expected))] string? doNotPopulateThisValue1 = null, [CallerArgumentExpression(nameof(precision))] string? doNotPopulateThisValue2 = null) + { + var assertionBuilder = valueSource.RegisterAssertion(new IsCloseToCondition(expected, precision) + , [doNotPopulateThisValue1, doNotPopulateThisValue2]); + + return new IsCloseToWrapper(assertionBuilder); + } +} + +public class IsCloseToWrapper(InvokableAssertionBuilder invokableAssertionBuilder) + : InvokableValueAssertionBuilder(invokableAssertionBuilder); + +file class IsCloseToCondition(TActual expected, TActual tolerance) : BaseAssertCondition + where TActual : + IFloatingPoint, + INumberBase +{ + protected override string GetExpectation() => $"to be within {tolerance} of {expected}"; + + protected override ValueTask GetResult( + TActual? actualValue, Exception? exception, + AssertionMetadata assertionMetadata + ) + { + if(actualValue is null) + return AssertionResult.Fail("received null"); + + TActual difference = actualValue - expected; + TActual absoluteDifference = TActual.Abs(difference); + bool isInRange = absoluteDifference <= tolerance; + return AssertionResult.FailIf(!isInRange, $"received {actualValue}"); + } +} diff --git a/tests/MaterialDesignThemes.UITests/TestBase.cs b/tests/MaterialDesignThemes.UITests/TestBase.cs index 66615f971c..e2aeac2535 100644 --- a/tests/MaterialDesignThemes.UITests/TestBase.cs +++ b/tests/MaterialDesignThemes.UITests/TestBase.cs @@ -1,7 +1,10 @@ using System.Diagnostics.CodeAnalysis; using System.Windows.Media; +using MaterialDesignThemes.UITests; +using TUnit.Core.Interfaces; + +[assembly: ParallelLimiter] -[assembly: CollectionBehavior(DisableTestParallelization = true)] [assembly: GenerateHelpers(typeof(AutoSuggestBox))] [assembly: GenerateHelpers(typeof(ColorPicker))] [assembly: GenerateHelpers(typeof(DecimalUpDown))] @@ -16,10 +19,15 @@ namespace MaterialDesignThemes.UITests; -public abstract class TestBase(ITestOutputHelper output) : IAsyncLifetime +public record SingleParallelLimit : IParallelLimit +{ + public int Limit => 1; +} + +public abstract class TestBase() { protected bool AttachedDebuggerToRemoteProcess { get; set; } = true; - protected ITestOutputHelper Output { get; } = output ?? throw new ArgumentNullException(nameof(output)); + protected static TextWriter Output => TestContext.Current?.OutputWriter ?? throw new InvalidOperationException("Could not find output writer"); [NotNull] protected IApp? App { get; set; } @@ -46,7 +54,8 @@ protected async Task LoadUserControl(Type userControlType) return await App.CreateWindowWithUserControl(userControlType); } - public async Task InitializeAsync() => + [Before(Test)] + public async ValueTask InitializeAsync() => App = await XamlTest.App.StartRemote(new AppOptions { #if !DEBUG @@ -55,5 +64,7 @@ public async Task InitializeAsync() => AllowVisualStudioDebuggerAttach = AttachedDebuggerToRemoteProcess, LogMessage = Output.WriteLine }); - public async Task DisposeAsync() => await App.DisposeAsync(); + + [After(Test)] + public async ValueTask DisposeAsync() => await App.DisposeAsync(); } diff --git a/tests/MaterialDesignThemes.UITests/WPF/AutoSuggestBoxes/AutoSuggestTextBoxTests.cs b/tests/MaterialDesignThemes.UITests/WPF/AutoSuggestBoxes/AutoSuggestTextBoxTests.cs index 59e2fc3f75..f1302b56bf 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/AutoSuggestBoxes/AutoSuggestTextBoxTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/AutoSuggestBoxes/AutoSuggestTextBoxTests.cs @@ -1,19 +1,17 @@ using System.ComponentModel; using MaterialDesignThemes.UITests.Samples.AutoSuggestBoxes; using MaterialDesignThemes.UITests.Samples.AutoSuggestTextBoxes; -using Xunit.Sdk; - +using TUnit.Core.Exceptions; namespace MaterialDesignThemes.UITests.WPF.AutoSuggestBoxes; public class AutoSuggestBoxTests : TestBase { - public AutoSuggestBoxTests(ITestOutputHelper output) - : base(output) + public AutoSuggestBoxTests() { - AttachedDebuggerToRemoteProcess = true; + AttachedDebuggerToRemoteProcess = false; } - [Fact] + [Test] public async Task CanFilterItems_WithSuggestionsAndDisplayMember_FiltersSuggestions() { await using var recorder = new TestRecorder(App); @@ -29,8 +27,8 @@ public async Task CanFilterItems_WithSuggestionsAndDisplayMember_FiltersSuggesti //Assert - Assert.True(await suggestBox.GetIsSuggestionOpen()); - Assert.True(await popup.GetIsOpen()); + await Assert.That(await suggestBox.GetIsSuggestionOpen()).IsTrue(); + await Assert.That(await popup.GetIsOpen()).IsTrue(); //Validates these elements are found await AssertExists(suggestionListBox, "Bananas"); @@ -44,7 +42,7 @@ public async Task CanFilterItems_WithSuggestionsAndDisplayMember_FiltersSuggesti recorder.Success(); } - [Fact] + [Test] public async Task CanChoiceItem_FromTheSuggestions_AssertTheTextUpdated() { await using var recorder = new TestRecorder(App); @@ -59,8 +57,8 @@ public async Task CanChoiceItem_FromTheSuggestions_AssertTheTextUpdated() await suggestBox.SendInput(new KeyboardInput("B")); //Assert - Assert.True(await suggestBox.GetIsSuggestionOpen()); - Assert.True(await popup.GetIsOpen()); + await Assert.That(await suggestBox.GetIsSuggestionOpen()).IsTrue(); + await Assert.That(await popup.GetIsOpen()).IsTrue(); double? lastHeight = null; await Wait.For(async () => @@ -71,7 +69,7 @@ await Wait.For(async () => lastHeight = currentHeight; if (!rv) { - await Task.Delay(100); + await Task.Delay(100, TestContext.Current!.CancellationToken); } return rv; }); @@ -82,16 +80,16 @@ await Wait.For(async () => await bananas.LeftClick(); // Wait for the text to be updated - await Task.Delay(50); + await Task.Delay(50, TestContext.Current!.CancellationToken); var suggestBoxText = await suggestBox.GetText(); //Validate that the current text is the same as the selected item - Assert.Equal("Bananas", suggestBoxText); + await Assert.That(suggestBoxText).IsEqualTo("Bananas"); recorder.Success(); } - [Fact] + [Test] public async Task CanFilterItems_WithCollectionView_FiltersSuggestions() { await using var recorder = new TestRecorder(App); @@ -108,8 +106,8 @@ public async Task CanFilterItems_WithCollectionView_FiltersSuggestions() //Assert - Assert.True(await suggestBox.GetIsSuggestionOpen()); - Assert.True(await popup.GetIsOpen()); + await Assert.That(await suggestBox.GetIsSuggestionOpen()).IsTrue(); + await Assert.That(await popup.GetIsOpen()).IsTrue(); //Validates these elements are found await AssertExists(suggestionListBox, "Bananas"); @@ -123,7 +121,7 @@ public async Task CanFilterItems_WithCollectionView_FiltersSuggestions() recorder.Success(); } - [Fact] + [Test] [Description("Issue 3761")] public async Task AutoSuggestBox_MovesFocusToNextElement_WhenPopupIsClosed() { @@ -144,22 +142,22 @@ public async Task AutoSuggestBox_MovesFocusToNextElement_WhenPopupIsClosed() // Act await suggestBox.MoveKeyboardFocus(); - await Task.Delay(50); + await Task.Delay(50, TestContext.Current!.CancellationToken); await suggestBox.SendInput(new KeyboardInput("B")); // Open the popup - await Task.Delay(50); + await Task.Delay(50, TestContext.Current.CancellationToken); await suggestBox.SendInput(new KeyboardInput(Key.Escape)); // Close the popup - await Task.Delay(50); + await Task.Delay(50, TestContext.Current.CancellationToken); await suggestBox.SendInput(new KeyboardInput(Key.Tab)); // Press TAB to focus the next element - await Task.Delay(50); + await Task.Delay(50, TestContext.Current.CancellationToken); // Assert - Assert.False(await suggestBox.GetIsFocused()); - Assert.True(await nextTextBox.GetIsFocused()); + await Assert.That(await suggestBox.GetIsFocused()).IsFalse(); + await Assert.That(await nextTextBox.GetIsFocused()).IsTrue(); recorder.Success(); } - [Fact] + [Test] [Description("Issue 3815")] public async Task AutoSuggestBox_KeysUpAndDown_WrapAround() { @@ -175,7 +173,7 @@ public async Task AutoSuggestBox_KeysUpAndDown_WrapAround() //Act & Assert await suggestBox.MoveKeyboardFocus(); await suggestBox.SendInput(new KeyboardInput("e")); - await Task.Delay(delay); + await Task.Delay(delay, TestContext.Current!.CancellationToken); static int? GetSuggestionCount(AutoSuggestBox autoSuggestBox) { @@ -187,20 +185,22 @@ public async Task AutoSuggestBox_KeysUpAndDown_WrapAround() //Assert that initially the first item is selected int selectedIndex = await suggestionListBox.GetSelectedIndex(); - Assert.Equal(0, selectedIndex); - await Task.Delay(delay); + await Assert.That(selectedIndex).IsEqualTo(0); + await Task.Delay(delay, TestContext.Current.CancellationToken); //Assert that the last item is selected after pressing ArrowUp await suggestBox.SendInput(new KeyboardInput(Key.Up)); - Assert.Equal(itemCount - 1, await suggestionListBox.GetSelectedIndex()); - await Task.Delay(delay); + await Assert.That(await suggestionListBox.GetSelectedIndex()).IsEqualTo(itemCount - 1); + await Task.Delay(delay, TestContext.Current.CancellationToken); //Assert that the first item is selected after pressing ArrowDown await suggestBox.SendInput(new KeyboardInput(Key.Down)); - Assert.Equal(0, await suggestionListBox.GetSelectedIndex()); + await Assert.That(await suggestionListBox.GetSelectedIndex()).IsEqualTo(0); + + recorder.Success(); } - [Fact] + [Test] [Description("Issue 3845")] public async Task AutoSuggestBox_SelectingAnItem_SetsSelectedItem() { @@ -220,12 +220,12 @@ public async Task AutoSuggestBox_SelectingAnItem_SetsSelectedItem() //Assert string? selectedItem = (await suggestBox.GetSelectedItem()) as string; - Assert.Equal("Bananas", selectedItem); + await Assert.That(selectedItem).IsEqualTo("Bananas"); - static void AssertViewModelProperty(AutoSuggestBox autoSuggestBox) + static async Task AssertViewModelProperty(AutoSuggestBox autoSuggestBox) { var viewModel = (AutoSuggestTextBoxWithCollectionViewViewModel)autoSuggestBox.DataContext; - Assert.Equal("Bananas", viewModel.SelectedItem); + await Assert.That(viewModel.SelectedItem).IsEqualTo("Bananas"); } await suggestBox.RemoteExecute(AssertViewModelProperty); @@ -237,11 +237,11 @@ private static async Task AssertExists(IVisualElement suggestionListBox try { _ = await suggestionListBox.GetElement(ElementQuery.PropertyExpression(x => x.Text, text)); - Assert.True(existsOrNotCheck); + await Assert.That(existsOrNotCheck).IsTrue(); } - catch (Exception e) when (e is not TrueException) + catch (Exception e) when (e is not TUnitException) { - Assert.False(existsOrNotCheck); + await Assert.That(existsOrNotCheck).IsFalse(); } } } diff --git a/tests/MaterialDesignThemes.UITests/WPF/Buttons/OutlineButtonTests.cs b/tests/MaterialDesignThemes.UITests/WPF/Buttons/OutlineButtonTests.cs index 1718fcc834..312ab51d28 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/Buttons/OutlineButtonTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/Buttons/OutlineButtonTests.cs @@ -4,11 +4,7 @@ namespace MaterialDesignThemes.UITests.WPF.Buttons; public class OutlineButtonTests : TestBase { - public OutlineButtonTests(ITestOutputHelper output) - : base(output) - { } - - [Fact] + [Test] public async Task OutlinedButton_UsesThemeColorForBorder() { await using var recorder = new TestRecorder(App); @@ -24,13 +20,13 @@ public async Task OutlinedButton_UsesThemeColorForBorder() Color? internalBorderColor = await internalBorder.GetBorderBrushColor(); //Assert - Assert.Equal(midColor, borderColor); - Assert.Equal(midColor, internalBorderColor); + await Assert.That(borderColor).IsEqualTo(midColor); + await Assert.That(internalBorderColor).IsEqualTo(midColor); recorder.Success(); } - [Fact] + [Test] public async Task OutlinedButton_BorderCanBeOverridden() { await using var recorder = new TestRecorder(App); @@ -50,13 +46,13 @@ public async Task OutlinedButton_BorderCanBeOverridden() Color? borderBrush = await internalBorder.GetBorderBrushColor(); //Assert - Assert.Equal(new Thickness(5), borderThickness); - Assert.Equal(Colors.Red, borderBrush); + await Assert.That(borderThickness).IsEqualTo(new Thickness(5)); + await Assert.That(borderBrush).IsEqualTo(Colors.Red); recorder.Success(); } - [Fact] + [Test] public async Task OutlinedButton_OnMouseOver_UsesThemeBrush() { await using var recorder = new TestRecorder(App); @@ -74,7 +70,7 @@ await Wait.For(async () => SolidColorBrush? internalBorderBackground = (await internalBorder.GetBackground()) as SolidColorBrush; //Assert - Assert.Equal(midColor, internalBorderBackground?.Color); + await Assert.That(internalBorderBackground?.Color).IsEqualTo(midColor); }); recorder.Success(); diff --git a/tests/MaterialDesignThemes.UITests/WPF/Buttons/RaisedButtonTests.cs b/tests/MaterialDesignThemes.UITests/WPF/Buttons/RaisedButtonTests.cs index c0ed522aec..39c7b3ea7a 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/Buttons/RaisedButtonTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/Buttons/RaisedButtonTests.cs @@ -4,11 +4,7 @@ namespace MaterialDesignThemes.UITests.WPF.Buttons; public class RaisedButtonTests : TestBase { - public RaisedButtonTests(ITestOutputHelper output) - : base(output) - { } - - [Fact] + [Test] public async Task OnLoad_ThemeBrushesSet() { await using var recorder = new TestRecorder(App); @@ -21,7 +17,7 @@ public async Task OnLoad_ThemeBrushesSet() Color? color = await button.GetBackgroundColor(); //Assert - Assert.Equal(midColor, color); + await Assert.That(color).IsEqualTo(midColor); recorder.Success(); } diff --git a/tests/MaterialDesignThemes.UITests/WPF/Cards/ElevatedCardTests.cs b/tests/MaterialDesignThemes.UITests/WPF/Cards/ElevatedCardTests.cs index f30f158a9e..1187395aee 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/Cards/ElevatedCardTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/Cards/ElevatedCardTests.cs @@ -1,12 +1,11 @@ -namespace MaterialDesignThemes.UITests.WPF.Cards; + + +namespace MaterialDesignThemes.UITests.WPF.Cards; public class ElevatedCardTests : TestBase { - public ElevatedCardTests(ITestOutputHelper output) - : base(output) - { } - [Fact] + [Test] public async Task ElevatedCard_UniformCornerRadiusApplied_AppliesCornerRadiusOnBorder() { await using var recorder = new TestRecorder(App); @@ -20,10 +19,10 @@ public async Task ElevatedCard_UniformCornerRadiusApplied_AppliesCornerRadiusOnB CornerRadius? internalBorderCornerRadius = await internalBorder.GetCornerRadius(); //Assert - Assert.Equal(5, internalBorderCornerRadius.Value.TopLeft); - Assert.Equal(5, internalBorderCornerRadius.Value.TopRight); - Assert.Equal(5, internalBorderCornerRadius.Value.BottomRight); - Assert.Equal(5, internalBorderCornerRadius.Value.BottomLeft); + await Assert.That(internalBorderCornerRadius.Value.TopLeft).IsEqualTo(5); + await Assert.That(internalBorderCornerRadius.Value.TopRight).IsEqualTo(5); + await Assert.That(internalBorderCornerRadius.Value.BottomRight).IsEqualTo(5); + await Assert.That(internalBorderCornerRadius.Value.BottomLeft).IsEqualTo(5); recorder.Success(); } diff --git a/tests/MaterialDesignThemes.UITests/WPF/Cards/OutlinedCardTests.cs b/tests/MaterialDesignThemes.UITests/WPF/Cards/OutlinedCardTests.cs index 424cbaf138..15da509acc 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/Cards/OutlinedCardTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/Cards/OutlinedCardTests.cs @@ -4,11 +4,7 @@ namespace MaterialDesignThemes.UITests.WPF.Cards; public class OutlinedCardTests : TestBase { - public OutlinedCardTests(ITestOutputHelper output) - : base(output) - { } - - [Fact] + [Test] public async Task OutlinedCard_UsesThemeColorForBorder() { await using var recorder = new TestRecorder(App); @@ -23,12 +19,12 @@ public async Task OutlinedCard_UsesThemeColorForBorder() Color? internalBorderColor = await internalBorder.GetBorderBrushColor(); //Assert - Assert.Equal(dividerColor, internalBorderColor); + await Assert.That(internalBorderColor).IsEqualTo(dividerColor); recorder.Success(); } - [Fact] + [Test] public async Task OutlinedCard_UniformCornerRadiusApplied_AppliesCornerRadiusOnBorder() { await using var recorder = new TestRecorder(App); @@ -42,10 +38,10 @@ public async Task OutlinedCard_UniformCornerRadiusApplied_AppliesCornerRadiusOnB CornerRadius? internalBorderCornerRadius = await internalBorder.GetCornerRadius(); //Assert - Assert.Equal(5, internalBorderCornerRadius.Value.TopLeft); - Assert.Equal(5, internalBorderCornerRadius.Value.TopRight); - Assert.Equal(5, internalBorderCornerRadius.Value.BottomRight); - Assert.Equal(5, internalBorderCornerRadius.Value.BottomLeft); + await Assert.That(internalBorderCornerRadius.Value.TopLeft).IsEqualTo(5); + await Assert.That(internalBorderCornerRadius.Value.TopRight).IsEqualTo(5); + await Assert.That(internalBorderCornerRadius.Value.BottomRight).IsEqualTo(5); + await Assert.That(internalBorderCornerRadius.Value.BottomLeft).IsEqualTo(5); recorder.Success(); } diff --git a/tests/MaterialDesignThemes.UITests/WPF/ColorPickerTests.cs b/tests/MaterialDesignThemes.UITests/WPF/ColorPickerTests.cs index 57805e8a1c..922a0b5cd8 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/ColorPickerTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/ColorPickerTests.cs @@ -5,12 +5,7 @@ namespace MaterialDesignThemes.UITests.WPF; public class ColorPickerTests : TestBase { - public ColorPickerTests(ITestOutputHelper output) - : base(output) - { - } - - [Fact] + [Test] public async Task OnLostFocusIfSelectedTimeIsNull_DatePartWillBeToday() { await using var recorder = new TestRecorder(App); @@ -29,12 +24,12 @@ public async Task OnLostFocusIfSelectedTimeIsNull_DatePartWillBeToday() await thumb.SendInput(MouseInput.MoveRelative(xOffset: -5, yOffset: -10)); await thumb.SendInput(MouseInput.LeftDown()); await thumb.SendInput(MouseInput.MoveRelative(yOffset: 25)); - await Task.Delay(100); + await Task.Delay(100, TestContext.Current!.CancellationToken); await thumb.SendInput(MouseInput.LeftUp()); double currentBrightness = (await colorPicker.GetColor()).ToHsb().Brightness; - Assert.True(currentBrightness < lastBrightness, $"Brightness {currentBrightness} is not less than {lastBrightness}"); + await Assert.That(currentBrightness).IsLessThan(lastBrightness); recorder.Success(); } diff --git a/tests/MaterialDesignThemes.UITests/WPF/ColorZones/ColorZoneTests.cs b/tests/MaterialDesignThemes.UITests/WPF/ColorZones/ColorZoneTests.cs index 6422947496..bc8dca69ca 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/ColorZones/ColorZoneTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/ColorZones/ColorZoneTests.cs @@ -1,26 +1,21 @@ using System.Windows.Media; + namespace MaterialDesignThemes.UITests.WPF.ColorZones; public class ColorZoneTests : TestBase { - public ColorZoneTests(ITestOutputHelper output) - : base(output) - { - - } - - [Theory] - [InlineData(ColorZoneMode.Standard, "MaterialDesign.Brush.Background", "MaterialDesign.Brush.Foreground")] - [InlineData(ColorZoneMode.Inverted, "MaterialDesign.Brush.Foreground", "MaterialDesign.Brush.Background")] - [InlineData(ColorZoneMode.PrimaryLight, "MaterialDesign.Brush.Primary.Light", "MaterialDesign.Brush.Primary.Light.Foreground")] - [InlineData(ColorZoneMode.PrimaryMid, "MaterialDesign.Brush.Primary", "MaterialDesign.Brush.Primary.Foreground")] - [InlineData(ColorZoneMode.PrimaryDark, "MaterialDesign.Brush.Primary.Dark", "MaterialDesign.Brush.Primary.Dark.Foreground")] - [InlineData(ColorZoneMode.SecondaryLight, "MaterialDesign.Brush.Secondary.Light", "MaterialDesign.Brush.Secondary.Light.Foreground")] - [InlineData(ColorZoneMode.SecondaryMid, "MaterialDesign.Brush.Secondary", "MaterialDesign.Brush.Secondary.Foreground")] - [InlineData(ColorZoneMode.SecondaryDark, "MaterialDesign.Brush.Secondary.Dark", "MaterialDesign.Brush.Secondary.Dark.Foreground")] - [InlineData(ColorZoneMode.Light, "MaterialDesign.Brush.ColorZone.LightBackground", "MaterialDesign.Brush.ColorZone.LightForeground")] - [InlineData(ColorZoneMode.Dark, "MaterialDesign.Brush.ColorZone.DarkBackground", "MaterialDesign.Brush.ColorZone.DarkForeground")] + [Test] + [Arguments(ColorZoneMode.Standard, "MaterialDesign.Brush.Background", "MaterialDesign.Brush.Foreground")] + [Arguments(ColorZoneMode.Inverted, "MaterialDesign.Brush.Foreground", "MaterialDesign.Brush.Background")] + [Arguments(ColorZoneMode.PrimaryLight, "MaterialDesign.Brush.Primary.Light", "MaterialDesign.Brush.Primary.Light.Foreground")] + [Arguments(ColorZoneMode.PrimaryMid, "MaterialDesign.Brush.Primary", "MaterialDesign.Brush.Primary.Foreground")] + [Arguments(ColorZoneMode.PrimaryDark, "MaterialDesign.Brush.Primary.Dark", "MaterialDesign.Brush.Primary.Dark.Foreground")] + [Arguments(ColorZoneMode.SecondaryLight, "MaterialDesign.Brush.Secondary.Light", "MaterialDesign.Brush.Secondary.Light.Foreground")] + [Arguments(ColorZoneMode.SecondaryMid, "MaterialDesign.Brush.Secondary", "MaterialDesign.Brush.Secondary.Foreground")] + [Arguments(ColorZoneMode.SecondaryDark, "MaterialDesign.Brush.Secondary.Dark", "MaterialDesign.Brush.Secondary.Dark.Foreground")] + [Arguments(ColorZoneMode.Light, "MaterialDesign.Brush.ColorZone.LightBackground", "MaterialDesign.Brush.ColorZone.LightForeground")] + [Arguments(ColorZoneMode.Dark, "MaterialDesign.Brush.ColorZone.DarkBackground", "MaterialDesign.Brush.ColorZone.DarkForeground")] public async Task Mode_SetsThemeColors(ColorZoneMode mode, string backgroundBrush, string foregroundBrush) { await using var recorder = new TestRecorder(App); @@ -31,8 +26,8 @@ public async Task Mode_SetsThemeColors(ColorZoneMode mode, string backgroundBrus Color background = await GetThemeColor(backgroundBrush); Color foreground = await GetThemeColor(foregroundBrush); - Assert.Equal(background, await colorZone.GetBackgroundColor()); - Assert.Equal(foreground, await colorZone.GetForegroundColor()); + await Assert.That(await colorZone.GetBackgroundColor()).IsEqualTo(background); + await Assert.That(await colorZone.GetForegroundColor()).IsEqualTo(foreground); recorder.Success(); } diff --git a/tests/MaterialDesignThemes.UITests/WPF/ComboBoxes/ComboBoxTests.cs b/tests/MaterialDesignThemes.UITests/WPF/ComboBoxes/ComboBoxTests.cs index d081596312..1510cf2b05 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/ComboBoxes/ComboBoxTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/ComboBoxes/ComboBoxTests.cs @@ -5,12 +5,7 @@ namespace MaterialDesignThemes.UITests.WPF.ComboBoxes; public class ComboBoxTests : TestBase { - public ComboBoxTests(ITestOutputHelper output) - : base(output) - { - } - - [Fact] + [Test] [Description("Pull Request 2192")] public async Task OnComboBoxHelperTextFontSize_ChangesHelperTextFontSize() { @@ -26,11 +21,11 @@ public async Task OnComboBoxHelperTextFontSize_ChangesHelperTextFontSize() double fontSize = await helpTextBlock.GetFontSize(); - Assert.Equal(20, fontSize); + await Assert.That(fontSize).IsEqualTo(20); recorder.Success(); } - [Fact] + [Test] [Description("Pull Request 2192")] public async Task OnFilledComboBoxHelperTextFontSize_ChangesHelperTextFontSize() { @@ -47,11 +42,11 @@ public async Task OnFilledComboBoxHelperTextFontSize_ChangesHelperTextFontSize() double fontSize = await helpTextBlock.GetFontSize(); - Assert.Equal(20, fontSize); + await Assert.That(fontSize).IsEqualTo(20); recorder.Success(); } - [Fact] + [Test] [Description("Issue 2495")] public async Task OnComboBox_WithClearButton_ClearsSelection() { @@ -74,23 +69,23 @@ public async Task OnComboBox_WithClearButton_ClearsSelection() int? selectedIndex = await comboBox.GetSelectedIndex(); object? text = await comboBox.GetText(); - Assert.True(selectedIndex >= 0); - Assert.NotNull(text); + await Assert.That(selectedIndex >= 0).IsTrue(); + await Assert.That(text).IsNotNull(); await clearButton.LeftClick(); await Wait.For(async () => { text = await comboBox.GetText(); - Assert.Null(text); + await Assert.That(text).IsNull(); selectedIndex = await comboBox.GetSelectedIndex(); - Assert.False(selectedIndex >= 0); + await Assert.That(selectedIndex >= 0).IsFalse(); }); recorder.Success(); } - [Fact] + [Test] [Description("Issue 2690")] public async Task OnEditableComboBox_WithDefaultContextMenu_ShowsCutCopyPaste() { @@ -108,7 +103,7 @@ public async Task OnEditableComboBox_WithDefaultContextMenu_ShowsCutCopyPaste() await comboBox.RightClick(); IVisualElement? contextMenu = await comboBox.GetContextMenu(); - Assert.True(contextMenu is not null, "No context menu set on the ComboBox"); + await Assert.That(contextMenu).IsNotNull().Because("No context menu set on the ComboBox"); await Wait.For(async () => await contextMenu!.GetIsVisible()); await AssertMenu("Cut"); await AssertMenu("Copy"); @@ -119,11 +114,11 @@ public async Task OnEditableComboBox_WithDefaultContextMenu_ShowsCutCopyPaste() async Task AssertMenu(string menuHeader) { var menuItem = await contextMenu!.GetElement(ElementQuery.PropertyExpression(x => x.Header, menuHeader)); - Assert.True(menuItem is not null, $"{menuHeader} menu item not found"); + await Assert.That(menuItem).IsNotNull().Because($"{menuHeader} menu item not found"); } } - [Fact] + [Test] [Description("Issue 2713")] public async Task OnEditableComboBox_ClickInTextArea_FocusesTextBox() { @@ -147,7 +142,7 @@ public async Task OnEditableComboBox_ClickInTextArea_FocusesTextBox() // Open the ComboBox initially await comboBox.LeftClick(Position.RightCenter); - await Task.Delay(50); // Allow a little time for the drop-down to open (and property to change) + await Task.Delay(50, TestContext.Current!.CancellationToken); // Allow a little time for the drop-down to open (and property to change) bool wasOpenAfterClickOnToggleButton = await comboBox.GetIsDropDownOpen(); // Focus (i.e. click) another element @@ -155,29 +150,29 @@ public async Task OnEditableComboBox_ClickInTextArea_FocusesTextBox() // Click the editable TextBox of the ComboBox await editableTextBox.LeftClick(); - await Task.Delay(50); // Allow a little time for the drop-down to open (and property to change) + await Task.Delay(50, TestContext.Current.CancellationToken); // Allow a little time for the drop-down to open (and property to change) bool wasOpenAfterClickOnEditableTextBox = await comboBox.GetIsDropDownOpen(); bool textBoxHasFocus = await editableTextBox.GetIsFocused(); bool textBoxHasKeyboardFocus = await editableTextBox.GetIsKeyboardFocused(); - Assert.True(wasOpenAfterClickOnToggleButton, "ComboBox should have opened drop down when clicking the toggle button"); - Assert.False(wasOpenAfterClickOnEditableTextBox, "ComboBox should not have opened drop down when clicking the editable TextBox"); - Assert.True(textBoxHasFocus, "Editable TextBox should have focus"); - Assert.True(textBoxHasKeyboardFocus, "Editable TextBox should have keyboard focus"); + await Assert.That(wasOpenAfterClickOnToggleButton).IsTrue().Because("ComboBox should have opened drop down when clicking the toggle button"); + await Assert.That(wasOpenAfterClickOnEditableTextBox).IsFalse().Because("ComboBox should not have opened drop down when clicking the editable ait wTextBox"); + await Assert.That(textBoxHasFocus).IsTrue().Because("Editable TextBox should have focus"); + await Assert.That(textBoxHasKeyboardFocus).IsTrue().Because("Editable TextBox should have keyboard focus"); recorder.Success(); } - [Theory] - [InlineData("MaterialDesignFloatingHintComboBox", null)] - [InlineData("MaterialDesignFloatingHintComboBox", 5)] - [InlineData("MaterialDesignFloatingHintComboBox", 20)] - [InlineData("MaterialDesignFilledComboBox", null)] - [InlineData("MaterialDesignFilledComboBox", 5)] - [InlineData("MaterialDesignFilledComboBox", 20)] - [InlineData("MaterialDesignOutlinedComboBox", null)] - [InlineData("MaterialDesignOutlinedComboBox", 5)] - [InlineData("MaterialDesignOutlinedComboBox", 20)] + [Test] + [Arguments("MaterialDesignFloatingHintComboBox", null)] + [Arguments("MaterialDesignFloatingHintComboBox", 5)] + [Arguments("MaterialDesignFloatingHintComboBox", 20)] + [Arguments("MaterialDesignFilledComboBox", null)] + [Arguments("MaterialDesignFilledComboBox", 5)] + [Arguments("MaterialDesignFilledComboBox", 20)] + [Arguments("MaterialDesignOutlinedComboBox", null)] + [Arguments("MaterialDesignOutlinedComboBox", 5)] + [Arguments("MaterialDesignOutlinedComboBox", 20)] public async Task ComboBox_WithHintAndHelperText_RespectsPadding(string styleName, int? padding) { await using var recorder = new TestRecorder(App); @@ -203,22 +198,22 @@ public async Task ComboBox_WithHintAndHelperText_RespectsPadding(string styleNam Rect? hintCoordinates = await hint.GetCoordinates(); Rect? helperTextCoordinates = await helperText.GetCoordinates(); - Assert.InRange(Math.Abs(contentHostCoordinates.Value.Left - hintCoordinates.Value.Left), 0, tolerance); - Assert.InRange(Math.Abs(contentHostCoordinates.Value.Left - helperTextCoordinates.Value.Left), 0, tolerance); + await Assert.That(Math.Abs(contentHostCoordinates.Value.Left - hintCoordinates.Value.Left)).IsCloseTo(0, tolerance); + await Assert.That(Math.Abs(contentHostCoordinates.Value.Left - helperTextCoordinates.Value.Left)).IsCloseTo(0, tolerance); recorder.Success(); } - [Theory] - [InlineData("MaterialDesignFloatingHintComboBox", null)] - [InlineData("MaterialDesignFloatingHintComboBox", 5)] - [InlineData("MaterialDesignFloatingHintComboBox", 20)] - [InlineData("MaterialDesignFilledComboBox", null)] - [InlineData("MaterialDesignFilledComboBox", 5)] - [InlineData("MaterialDesignFilledComboBox", 20)] - [InlineData("MaterialDesignOutlinedComboBox", null)] - [InlineData("MaterialDesignOutlinedComboBox", 5)] - [InlineData("MaterialDesignOutlinedComboBox", 20)] + [Test] + [Arguments("MaterialDesignFloatingHintComboBox", null)] + [Arguments("MaterialDesignFloatingHintComboBox", 5)] + [Arguments("MaterialDesignFloatingHintComboBox", 20)] + [Arguments("MaterialDesignFilledComboBox", null)] + [Arguments("MaterialDesignFilledComboBox", 5)] + [Arguments("MaterialDesignFilledComboBox", 20)] + [Arguments("MaterialDesignOutlinedComboBox", null)] + [Arguments("MaterialDesignOutlinedComboBox", 5)] + [Arguments("MaterialDesignOutlinedComboBox", 20)] public async Task ComboBox_WithHintAndValidationError_RespectsPadding(string styleName, int? padding) { await using var recorder = new TestRecorder(App); @@ -252,17 +247,17 @@ public async Task ComboBox_WithHintAndValidationError_RespectsPadding(string sty Rect? hintCoordinates = await hint.GetCoordinates(); Rect? errorViewerCoordinates = await errorViewer.GetCoordinates(); - Assert.InRange(Math.Abs(contentHostCoordinates.Value.Left - hintCoordinates.Value.Left), 0, tolerance); - Assert.InRange(Math.Abs(contentHostCoordinates.Value.Left - errorViewerCoordinates.Value.Left), 0, tolerance); + await Assert.That(Math.Abs(contentHostCoordinates.Value.Left - hintCoordinates.Value.Left)).IsCloseTo(0, tolerance); + await Assert.That(Math.Abs(contentHostCoordinates.Value.Left - errorViewerCoordinates.Value.Left)).IsCloseTo(0, tolerance); recorder.Success(); } - [Theory] - [InlineData(HorizontalAlignment.Left)] - [InlineData(HorizontalAlignment.Right)] - [InlineData(HorizontalAlignment.Center)] - [InlineData(HorizontalAlignment.Stretch)] + [Test] + [Arguments(HorizontalAlignment.Left)] + [Arguments(HorizontalAlignment.Right)] + [Arguments(HorizontalAlignment.Center)] + [Arguments(HorizontalAlignment.Stretch)] [Description("Issue 3433")] public async Task ComboBox_WithHorizontalContentAlignment_RespectsAlignment(HorizontalAlignment alignment) { @@ -278,15 +273,15 @@ public async Task ComboBox_WithHorizontalContentAlignment_RespectsAlignment(Hori var comboBox = await stackPanel.GetElement("/ComboBox"); var selectedItemPresenter = await comboBox.GetElement("contentPresenter"); - Assert.Equal(alignment, await selectedItemPresenter.GetHorizontalAlignment()); + await Assert.That(await selectedItemPresenter.GetHorizontalAlignment()).IsEqualTo(alignment); recorder.Success(); } - [Theory] - [InlineData("MaterialDesignFloatingHintComboBox", 0, 0, 0, 1)] - [InlineData("MaterialDesignFilledComboBox", 0, 0, 0, 1)] - [InlineData("MaterialDesignOutlinedComboBox", 2, 2, 2, 2)] + [Test] + [Arguments("MaterialDesignFloatingHintComboBox", 0, 0, 0, 1)] + [Arguments("MaterialDesignFilledComboBox", 0, 0, 0, 1)] + [Arguments("MaterialDesignOutlinedComboBox", 2, 2, 2, 2)] [Description("Issue 3623")] public async Task ComboBox_BorderShouldDependOnAppliedStyle(string style, double left, double top, double right, double bottom) { @@ -305,7 +300,7 @@ public async Task ComboBox_BorderShouldDependOnAppliedStyle(string style, double await comboBox.LeftClick(); Thickness thickness = await border.GetBorderThickness(); - Assert.Equal(new Thickness(left, top, right, bottom), thickness); + await Assert.That(thickness).IsEqualTo(new Thickness(left, top, right, bottom)); recorder.Success(); } diff --git a/tests/MaterialDesignThemes.UITests/WPF/ContentControls/ContentControlTests.cs b/tests/MaterialDesignThemes.UITests/WPF/ContentControls/ContentControlTests.cs index b1e8b97c56..5350a69079 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/ContentControls/ContentControlTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/ContentControls/ContentControlTests.cs @@ -1,16 +1,11 @@ using System.ComponentModel; + namespace MaterialDesignThemes.UITests.WPF.ContentControls; public class ContentControlTests : TestBase { - public ContentControlTests(ITestOutputHelper output) - : base(output) - { - } - - - [Fact] + [Test] [Description("Issue 2510")] public async Task ClearButton_InsideOfControlTemplate_CanStillClearContent() { @@ -34,10 +29,10 @@ public async Task ClearButton_InsideOfControlTemplate_CanStillClearContent() await clearButton.LeftClick(); //Assert - Assert.Equal("Some Text", initial); + await Assert.That(initial).IsEqualTo("Some Text"); await Wait.For(async () => { - Assert.True(string.IsNullOrEmpty(await textBox.GetText())); + await Assert.That(await textBox.GetText()).IsNullOrEmpty(); }); recorder.Success(); diff --git a/tests/MaterialDesignThemes.UITests/WPF/DatePickers/DatePickerTests.cs b/tests/MaterialDesignThemes.UITests/WPF/DatePickers/DatePickerTests.cs index c4255d5c8d..f5a210c7b0 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/DatePickers/DatePickerTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/DatePickers/DatePickerTests.cs @@ -3,16 +3,12 @@ using System.Windows.Media; using MaterialDesignThemes.UITests.WPF.TextBoxes; + namespace MaterialDesignThemes.UITests.WPF.DatePickers; public class DatePickerTests : TestBase { - public DatePickerTests(ITestOutputHelper output) - : base(output) - { - } - - [Fact] + [Test] [Description("Pull Request 2192")] public async Task OnDatePickerHelperTextFontSize_ChangesHelperTextFontSize() { @@ -28,11 +24,11 @@ public async Task OnDatePickerHelperTextFontSize_ChangesHelperTextFontSize() double fontSize = await helpTextBlock.GetFontSize(); - Assert.Equal(20, fontSize); + await Assert.That(fontSize).IsEqualTo(20); recorder.Success(); } - [Fact] + [Test] [Description("Issue 2495")] public async Task OnDatePicker_WithClearButton_ClearsSelectedDate() { @@ -48,20 +44,20 @@ public async Task OnDatePicker_WithClearButton_ClearsSelectedDate() DateTime? selectedDate = await datePicker.GetSelectedDate(); - Assert.NotNull(selectedDate); + await Assert.That(selectedDate).IsNotNull(); await clearButton.LeftClick(); await Wait.For(async () => { selectedDate = await datePicker.GetSelectedDate(); - Assert.Null(selectedDate); + await Assert.That(selectedDate).IsNull(); }); recorder.Success(); } - [Fact] + [Test] [Description("Issue 3369")] public async Task OnDatePicker_WithClearButton_ClearsSelectedUncommittedText() { @@ -78,19 +74,19 @@ public async Task OnDatePicker_WithClearButton_ClearsSelectedUncommittedText() var clearButton = await datePicker.GetElement