From dc29f21d0406c78722089ac3c57929fb4ca23569 Mon Sep 17 00:00:00 2001 From: Chris Winland Date: Thu, 27 Jul 2023 16:20:21 -0400 Subject: [PATCH] Incorporate dialog content without having to code the buttons. Support UserControl and IDialog --- src/ButtonTemplateControl.xaml | 15 ++ src/ButtonTemplateControl.xaml.cs | 42 ++++ src/Dialogs/ContentDialog.xaml | 31 +++ src/Dialogs/ContentDialog.xaml.cs | 216 ++++++++++++++++++ src/Dialogs/DialogBase.cs | 38 ++- src/Dialogs/InfoDialog.cs | 10 +- src/DialogsDictionary.xaml | 22 +- wpf-material-dialogs.test/DialogTests.cs | 2 - wpf-material-wpfTest/CustomDialogs.xaml | 62 ++--- wpf-material-wpfTest/MainWindow.xaml | 20 +- wpf-material-wpfTest/MainWindow.xaml.cs | 45 +++- wpf-material-wpfTest/TestCustomDialog.xaml | 13 +- wpf-material-wpfTest/TestCustomDialog.xaml.cs | 71 +++++- .../Properties/AssemblyInfo.cs | 1 - 14 files changed, 507 insertions(+), 81 deletions(-) create mode 100644 src/ButtonTemplateControl.xaml create mode 100644 src/ButtonTemplateControl.xaml.cs create mode 100644 src/Dialogs/ContentDialog.xaml create mode 100644 src/Dialogs/ContentDialog.xaml.cs diff --git a/src/ButtonTemplateControl.xaml b/src/ButtonTemplateControl.xaml new file mode 100644 index 0000000..cbbb49e --- /dev/null +++ b/src/ButtonTemplateControl.xaml @@ -0,0 +1,15 @@ + + + diff --git a/src/ButtonTemplateControl.xaml.cs b/src/ButtonTemplateControl.xaml.cs new file mode 100644 index 0000000..fb31671 --- /dev/null +++ b/src/ButtonTemplateControl.xaml.cs @@ -0,0 +1,42 @@ +using System.Windows; +using System.Windows.Controls; +using wpf_material_dialogs.Interfaces; + +namespace wpf_material_dialogs +{ + /// + /// + /// Interaction logic for ButtonTemplateControl.xaml + /// + public partial class ButtonTemplateControl : UserControl + { + /// + /// The button alignment property + /// + public static readonly DependencyProperty ButtonAlignmentProperty = DependencyProperty.Register( + nameof(ButtonAlignment), typeof(HorizontalAlignment), typeof(ButtonTemplateControl), new PropertyMetadata(HorizontalAlignment.Right)); + + public static readonly DependencyProperty SelectedButtonsProperty = DependencyProperty.Register( + nameof(SelectedButtons), typeof(IButtons), typeof(ButtonTemplateControl), new PropertyMetadata(default(IButtons))); + + public IButtons SelectedButtons + { + get => (IButtons) GetValue(SelectedButtonsProperty); + set => SetValue(SelectedButtonsProperty, value); + } + /// + /// Gets or sets the button alignment. + /// + /// The button alignment. + public HorizontalAlignment ButtonAlignment + { + get => (HorizontalAlignment) GetValue(ButtonAlignmentProperty); + set => SetValue(ButtonAlignmentProperty, value); + } + + public ButtonTemplateControl() + { + InitializeComponent(); + } + } +} diff --git a/src/Dialogs/ContentDialog.xaml b/src/Dialogs/ContentDialog.xaml new file mode 100644 index 0000000..8cf40f1 --- /dev/null +++ b/src/Dialogs/ContentDialog.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Dialogs/ContentDialog.xaml.cs b/src/Dialogs/ContentDialog.xaml.cs new file mode 100644 index 0000000..ce92879 --- /dev/null +++ b/src/Dialogs/ContentDialog.xaml.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Windows; +using System.Windows.Controls; +using wpf_material_dialogs.Enums; +using wpf_material_dialogs.Interfaces; + +namespace wpf_material_dialogs.Dialogs +{ + /// + /// Interaction logic for ContentDialog.xaml + /// + /// + public partial class ContentDialog : UserControl, INotifyPropertyChanged + { + #region Events + + /// + /// + /// Occurs when [property changed]. + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Fields + + /// + /// The content control property + /// + public static readonly DependencyProperty ContentControlProperty = DependencyProperty.Register( + nameof(ContentControl), typeof(object), typeof(ContentDialog), new FrameworkPropertyMetadata( + default, FrameworkPropertyMetadataOptions.AffectsParentArrange | + FrameworkPropertyMetadataOptions.AffectsParentMeasure | + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsMeasure, Refresh)); + + /// + /// The content control property + /// + public static readonly DependencyProperty DialogButtonsProperty = DependencyProperty.Register( + nameof(DialogButtons), typeof(DialogButton), typeof(ContentDialog), new FrameworkPropertyMetadata( + DialogButton.OkButtons, FrameworkPropertyMetadataOptions.AffectsParentArrange | + FrameworkPropertyMetadataOptions.AffectsParentMeasure | + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsMeasure, Refresh)); + + /// + /// The button alignment property + /// + public static readonly DependencyProperty ButtonAlignmentProperty = DependencyProperty.Register( + nameof(ButtonAlignment), typeof(HorizontalAlignment), typeof(ContentDialog), new PropertyMetadata(HorizontalAlignment.Right, Refresh)); + + /// + /// The buttons property + /// + public static readonly DependencyProperty ButtonsProperty = DependencyProperty.Register( + nameof(Buttons), typeof(IButtons), typeof(ContentDialog), new PropertyMetadata(default(IButtons), Refresh)); + + /// + /// The show buttons property + /// + public static readonly DependencyProperty ShowButtonsProperty = DependencyProperty.Register( + nameof(ShowButtons), typeof(bool), typeof(ContentDialog), new PropertyMetadata(true, Refresh)); + + private readonly object buttonLock = new(); + private readonly List buttonTypes = new(); + + #endregion + + #region Properties + + /// + /// Gets or sets a value indicating whether [show buttons]. + /// + /// true if [show buttons]; otherwise, false. + public bool ShowButtons + { + get => (bool) GetValue(ShowButtonsProperty); + set => SetValue(ShowButtonsProperty, value); + } + + /// + /// Gets or sets the buttons. + /// + /// The buttons. + public IButtons Buttons + { + get => (IButtons) GetValue(ButtonsProperty); + set => SetValue(ButtonsProperty, value); + } + + /// + /// Gets or sets the button alignment. + /// + /// The button alignment. + public HorizontalAlignment ButtonAlignment + { + get => (HorizontalAlignment) GetValue(ButtonAlignmentProperty); + set => SetValue(ButtonAlignmentProperty, value); + } + + /// + /// Gets the selected buttons based on the selection. + /// + /// . + public IButtons SelectedButtons => ShowButtons + ? DialogButtons == DialogButton.Custom + ? Buttons + : GetButtons(DialogButtons) + : null; + + /// + /// Gets or sets the content control. + /// + /// The content control. + public object ContentControl + { + get => GetValue(ContentControlProperty); + set => SetValue(ContentControlProperty, value); + } + + /// + /// Gets or sets the dialog buttons. + /// + /// The dialog buttons. + public DialogButton DialogButtons + { + get => (DialogButton) GetValue(DialogButtonsProperty); + set + { + SetValue(DialogButtonsProperty, value); + NotifyOfPropertyChanged(nameof(SelectedButtons)); + } + } + + #endregion + + /// + /// + /// Initializes a new instance of the class. + /// + public ContentDialog() => InitializeComponent(); + + /// + /// + /// Initializes a new instance of the class. + /// + /// The content control. + public ContentDialog(UserControl contentControl) : this() => ContentControl = contentControl; + + /// + /// + /// Initializes a new instance of the class. + /// + /// The content control. + public ContentDialog(IDialog contentControl) : this() => ContentControl = contentControl; + + /// + /// Gets the buttons. + /// + /// The button enum to convert to an IButton. + /// IButtons. + /// button + /// button + /// button + /// button + public IButtons GetButtons(DialogButton button) + { + // ReSharper disable once PossibleNullReferenceException + lock (buttonLock) + { + var assembly = Assembly.GetExecutingAssembly(); + + if (!buttonTypes?.Any() ?? false) + { + buttonTypes.AddRange(assembly + .GetTypes() + .Where(mytype => mytype.GetInterfaces().Contains(typeof(IButtons)))); + } + + var selectedButtonType = + buttonTypes?.First(mytype => button.ToString().Equals(mytype?.Name, StringComparison.CurrentCultureIgnoreCase)); + + return selectedButtonType?.FullName != null + ? assembly.CreateInstance(selectedButtonType.FullName) as IButtons + : throw new ArgumentOutOfRangeException(nameof(button)); + } + } + + /// + /// Notifies the of property changed. + /// + /// Name of the property. + /// + public void NotifyOfPropertyChanged([CallerMemberName] string propertyName = "") => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + /// + /// Refreshes this instance. + /// + internal void Refresh() => NotifyOfPropertyChanged(nameof(SelectedButtons)); + + private static void Refresh(DependencyObject o, DependencyPropertyChangedEventArgs _) + { + if (o is ContentDialog d) + { + d.Refresh(); + } + } + } +} \ No newline at end of file diff --git a/src/Dialogs/DialogBase.cs b/src/Dialogs/DialogBase.cs index e75a420..9fe859c 100644 --- a/src/Dialogs/DialogBase.cs +++ b/src/Dialogs/DialogBase.cs @@ -52,6 +52,18 @@ public abstract class DialogBase : IDialog private double titleFontSize = 16; private FontWeight titleFontWeight = FontWeights.Bold; private double width = double.NaN; + private bool showButtons = true; + + public bool ShowButtons + { + get => showButtons; + set + { + showButtons = value; + NotifyOfPropertyChanged(); + NotifyOfPropertyChanged(nameof(SelectedButtons)); + } + } #endregion @@ -61,9 +73,10 @@ public abstract class DialogBase : IDialog /// Gets the selected buttons based on the selection. /// /// . - public IButtons SelectedButtons => DialogButtons == DialogButton.Custom + public IButtons SelectedButtons => ShowButtons ? DialogButtons == DialogButton.Custom ? Buttons - : GetButtons(DialogButtons); + : GetButtons(DialogButtons) + : null; public FontWeight TitleFontWeight { @@ -105,10 +118,11 @@ public double Height } } + /// /// /// Gets or sets the button alignment. /// - /// . + /// . public HorizontalAlignment ButtonAlignment { get => buttonAlignment; @@ -119,11 +133,12 @@ public HorizontalAlignment ButtonAlignment } } + /// /// - /// Gets or sets the custom buttons to be used. must set to custom for this property to + /// Gets or sets the custom buttons to be used. must set to custom for this property to /// apply. /// - /// . + /// . public IButtons Buttons { get => buttons; @@ -131,13 +146,15 @@ public IButtons Buttons { buttons = value; NotifyOfPropertyChanged(); + NotifyOfPropertyChanged(nameof(SelectedButtons)); } } + /// /// /// Gets or sets the buttons to use in the dialog. /// - /// . + /// . public DialogButton DialogButtons { get => dialogButtons; @@ -145,9 +162,11 @@ public DialogButton DialogButtons { dialogButtons = value; NotifyOfPropertyChanged(); + NotifyOfPropertyChanged(nameof(SelectedButtons)); } } + /// /// /// Gets or sets the icon brush. /// @@ -162,6 +181,7 @@ public Brush IconBrush } } + /// /// /// Gets or sets the kind of the icon. /// @@ -176,6 +196,7 @@ public PackIconKind IconKind } } + /// /// /// Gets or sets a value indicating whether to show the icon. /// @@ -190,6 +211,7 @@ public bool ShowIcon } } + /// /// /// Gets or sets the dialog primary text. /// @@ -204,6 +226,7 @@ public string Text } } + /// /// /// Gets or sets the size of the text font. /// @@ -218,6 +241,7 @@ public double TextFontSize } } + /// /// /// Gets or sets the dialog title. /// @@ -232,6 +256,7 @@ public string Title } } + /// /// /// Gets or sets the size of the title font. /// @@ -341,6 +366,7 @@ public IButtons GetButtons(DialogButton button) } } + /// /// /// Notifies the of property changed. /// diff --git a/src/Dialogs/InfoDialog.cs b/src/Dialogs/InfoDialog.cs index 0553463..210b1c3 100644 --- a/src/Dialogs/InfoDialog.cs +++ b/src/Dialogs/InfoDialog.cs @@ -1,17 +1,17 @@ using MaterialDesignThemes.Wpf; -using System.ComponentModel; using System.Windows.Media; using wpf_material_dialogs.Enums; namespace wpf_material_dialogs.Dialogs { + /// /// /// Information Dialog. - /// Implements the - /// Implements the + /// Implements the + /// Implements the /// - /// - /// + /// + /// public class InfoDialog : DialogBase { /// diff --git a/src/DialogsDictionary.xaml b/src/DialogsDictionary.xaml index f29215f..91b26e1 100644 --- a/src/DialogsDictionary.xaml +++ b/src/DialogsDictionary.xaml @@ -1,7 +1,8 @@  + xmlns:dialogs="clr-namespace:wpf_material_dialogs.Dialogs" + xmlns:wpfMaterialDialogs="clr-namespace:wpf_material_dialogs"> @@ -69,16 +70,15 @@ FontWeight="{Binding FontWeight}" /> - - - + \ No newline at end of file diff --git a/wpf-material-dialogs.test/DialogTests.cs b/wpf-material-dialogs.test/DialogTests.cs index 8723f54..cc61f0b 100644 --- a/wpf-material-dialogs.test/DialogTests.cs +++ b/wpf-material-dialogs.test/DialogTests.cs @@ -203,5 +203,3 @@ internal class WarningDialogTests : DialogTestBase { } } - - diff --git a/wpf-material-wpfTest/CustomDialogs.xaml b/wpf-material-wpfTest/CustomDialogs.xaml index d9ef87b..a7260f2 100644 --- a/wpf-material-wpfTest/CustomDialogs.xaml +++ b/wpf-material-wpfTest/CustomDialogs.xaml @@ -3,57 +3,45 @@ xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:dialogs="clr-namespace:wpf_material_dialogs.Dialogs;assembly=wpf-material-dialogs" xmlns:wpfMaterialWpfTest="clr-namespace:wpf_material_wpfTest" - xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"> - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - + + Show Inner Buttons + + + + + + diff --git a/wpf-material-wpfTest/MainWindow.xaml.cs b/wpf-material-wpfTest/MainWindow.xaml.cs index 3856c7a..c7f1c68 100644 --- a/wpf-material-wpfTest/MainWindow.xaml.cs +++ b/wpf-material-wpfTest/MainWindow.xaml.cs @@ -4,6 +4,7 @@ using System.Windows.Forms; using System.Windows.Input; using wpf_material_dialogs.Dialogs; +using wpf_material_dialogs.Enums; using HorizontalAlignment = System.Windows.HorizontalAlignment; namespace wpf_material_wpfTest @@ -16,32 +17,66 @@ public partial class MainWindow : Window { #region Properties + public bool ShowButtons { get; set; } = false; + public string SubtitleText { get; set; } = + "This is a custom dialog provided by the test application. It can use regular dialog parameters or custom parameters, colors, buttons or reuse parts of the built-in dialog."; + public ICommand OpenDialog => new AsyncRelayCommand(async () => { var result = await DialogBase.ShowDialogContent(new CustomDialog { - SubTitle = - "This is a custom dialog provided by the test application. It can use regular dialog parameters or custom parameters, colors, buttons or reuse parts of the built-in dialog.", + SubTitle = SubtitleText, }); - await ShowResult(result); + await ShowResultAsync(result); }); public ICommand OpenDialog2 => new AsyncRelayCommand(async () => { var result = await DialogBase.ShowDialogContent(new TestCustomDialog()); - await ShowResult(result); + await ShowResultAsync(result); + }); + + public ICommand OpenContentDialog => new AsyncRelayCommand(async () => + { + var result = await DialogBase.ShowDialogContent(new ContentDialog( + new TestCustomDialog() { ShowButtons = ShowButtons}) + { + DialogButtons = DialogButton.YesNoCancelButtons, + }); + + await ShowResultAsync(result); + }); + + public ICommand OpenContentDialog2 => new AsyncRelayCommand(async () => + { + var result = await DialogBase.ShowDialogContent(new ContentDialog( + new CustomDialog() + { + ShowButtons = ShowButtons, + SubTitle = SubtitleText, + }) + { + DialogButtons = DialogButton.YesNoCancelButtons, + }); + + await ShowResultAsync(result); }); #endregion public MainWindow() => InitializeComponent(); - private static async Task ShowResult(DialogResult result) => await new InfoDialog + private static async Task ShowResultAsync(DialogResult result) => await new InfoDialog { Text = $"You pressed {result}", ButtonAlignment = HorizontalAlignment.Center, }.ShowDialog(); + + private void ToggleButton_OnChecked(object sender, RoutedEventArgs e) + { + + } } } \ No newline at end of file diff --git a/wpf-material-wpfTest/TestCustomDialog.xaml b/wpf-material-wpfTest/TestCustomDialog.xaml index ef9aad3..f21189a 100644 --- a/wpf-material-wpfTest/TestCustomDialog.xaml +++ b/wpf-material-wpfTest/TestCustomDialog.xaml @@ -4,6 +4,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:wpfMaterialDialogs="clr-namespace:wpf_material_dialogs;assembly=wpf-material-dialogs" mc:Ignorable="d" Height="Auto" Width="500" d:DesignHeight="450" d:DesignWidth="500"> @@ -25,9 +27,12 @@ Dialog Title Dialog text or data goes here - - - - + diff --git a/wpf-material-wpfTest/TestCustomDialog.xaml.cs b/wpf-material-wpfTest/TestCustomDialog.xaml.cs index 262ccf3..3474849 100644 --- a/wpf-material-wpfTest/TestCustomDialog.xaml.cs +++ b/wpf-material-wpfTest/TestCustomDialog.xaml.cs @@ -1,29 +1,86 @@ using CommunityToolkit.Mvvm.Input; using MaterialDesignThemes.Wpf; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Input; +using wpf_material_dialogs.Buttons; using wpf_material_dialogs.Interfaces; +using HorizontalAlignment = System.Windows.HorizontalAlignment; using UserControl = System.Windows.Controls.UserControl; namespace wpf_material_wpfTest { - /// + /// /// /// - /// Interaction logic for TestCustomDialog.xaml + /// Interaction logic for TestCustomDialog.xaml /// - public partial class TestCustomDialog : UserControl + public partial class TestCustomDialog : UserControl, INotifyPropertyChanged { - public ICommand CloseCommand => new AsyncRelayCommand((result) => + #region Events + + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Fields + + private bool showButtons = true; + + #endregion + + #region Properties + + public ICommand CloseCommand => new AsyncRelayCommand(result => { DialogHost.CloseDialogCommand.Execute(result, this); return Task.CompletedTask; }); - public TestCustomDialog() + public bool ShowButtons { - InitializeComponent(); + get => showButtons; + set + { + if (value == showButtons) + { + return; + } + + showButtons = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(SelectedButtons)); + } + } + + public IButtons SelectedButtons => new AbortRetryIgnoreButtons(); + + /// + /// Gets or sets the button alignment. + /// + /// The button alignment. + public HorizontalAlignment ButtonAlignment { get; set; } = HorizontalAlignment.Right; + + #endregion + + public TestCustomDialog() => InitializeComponent(); + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) + { + return false; + } + + field = value; + OnPropertyChanged(propertyName); + return true; } } -} +} \ No newline at end of file diff --git a/wpf-material-wpftestFramework/Properties/AssemblyInfo.cs b/wpf-material-wpftestFramework/Properties/AssemblyInfo.cs index 0424514..65397fd 100644 --- a/wpf-material-wpftestFramework/Properties/AssemblyInfo.cs +++ b/wpf-material-wpftestFramework/Properties/AssemblyInfo.cs @@ -30,7 +30,6 @@ //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] - [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located //(used if a resource is not found in the page,