diff --git a/source/CustomMessageBoxDemo/App.xaml.cs b/source/CustomMessageBoxDemo/App.xaml.cs index 7536b64..ed4cf90 100644 --- a/source/CustomMessageBoxDemo/App.xaml.cs +++ b/source/CustomMessageBoxDemo/App.xaml.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Windows; +using System.Windows; namespace CustomMessageBoxDemo { diff --git a/source/CustomMessageBoxDemo/CustomMessageBox Demo.csproj b/source/CustomMessageBoxDemo/CustomMessageBox Demo.csproj index ff5c399..3c5efc7 100644 --- a/source/CustomMessageBoxDemo/CustomMessageBox Demo.csproj +++ b/source/CustomMessageBoxDemo/CustomMessageBox Demo.csproj @@ -1,5 +1,5 @@  - + Debug x86 @@ -10,42 +10,26 @@ Properties CustomMessageBoxDemo CustomMessageBoxDemo - v4.0 - Client + v4.7.2 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AnyCPU bin\Debug\ + DEBUG + true AnyCPU bin\Release\ + true + diff --git a/source/CustomMessageBoxDemo/MainWindow.xaml b/source/CustomMessageBoxDemo/MainWindow.xaml index 6e87820..1f909fa 100644 --- a/source/CustomMessageBoxDemo/MainWindow.xaml +++ b/source/CustomMessageBoxDemo/MainWindow.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowStartupLocation="CenterScreen" - Title="MainWindow" Height="296" Width="550" Loaded="Window_Loaded"> + Title="MainWindow" Height="340" Width="570" Loaded="Window_Loaded"> + + + + + + + + + + + + + + + + + + diff --git a/source/WPFCustomMessageBox/CustomMessageBoxView.xaml.cs b/source/WPFCustomMessageBox/CustomMessageBoxView.xaml.cs new file mode 100644 index 0000000..cac3618 --- /dev/null +++ b/source/WPFCustomMessageBox/CustomMessageBoxView.xaml.cs @@ -0,0 +1,12 @@ +using System.Windows; + +namespace WPFCustomMessageBox +{ + internal partial class CustomMessageBoxView : Window + { + internal CustomMessageBoxView() + { + this.InitializeComponent(); + } + } +} diff --git a/source/WPFCustomMessageBox/CustomMessageBoxViewModel.cs b/source/WPFCustomMessageBox/CustomMessageBoxViewModel.cs new file mode 100644 index 0000000..1d70c71 --- /dev/null +++ b/source/WPFCustomMessageBox/CustomMessageBoxViewModel.cs @@ -0,0 +1,146 @@ +using System.ComponentModel; +using System.Windows; +using System.Windows.Media; + +namespace WPFCustomMessageBox +{ + internal class CustomMessageBoxViewModel : INotifyPropertyChanged + { + #region Constants + + private static double ButtonMinWidth => 88; + + private static double ButtonMaxWidth => 160; + + #endregion + + #region Properties that can be updated while the message box is open + + public string Caption + { + get => this.caption; + set + { + this.caption = value; + this.OnPropertyChanged(nameof(this.Caption)); + } + } + private string caption = "Message"; + + public string Message + { + get => this.message; + set + { + this.message = value; + this.OnPropertyChanged(nameof(this.Message)); + } + } + private string message = string.Empty; + + public string CancelButtonCaption + { + get => this.cancelButtonCaption; + set + { + this.cancelButtonCaption = value; + this.OnPropertyChanged(nameof(this.CancelButtonCaption)); + } + } + private string cancelButtonCaption = "_Cancel"; + + public string NoButtonCaption + { + get => this.noButtonCaption; + set + { + this.noButtonCaption = value; + this.OnPropertyChanged(nameof(this.NoButtonCaption)); + } + } + private string noButtonCaption = "_No"; + + public string YesButtonCaption + { + get => this.yesButtonCaption; + set + { + this.yesButtonCaption = value; + this.OnPropertyChanged(nameof(this.YesButtonCaption)); + } + } + private string yesButtonCaption = "_Yes"; + + public string OkButtonCaption + { + get => this.okButtonCaption; + set + { + this.okButtonCaption = value; + this.OnPropertyChanged(nameof(this.OkButtonCaption)); + } + } + private string okButtonCaption = "_OK"; + + #endregion + + #region Fixed properties + + public double MaxWidth { get; set; } = 470; + + public double MinWidth { get; set; } = 154; + + public double MaxHeight { get; set; } = 600; + + public double MinHeight { get; set; } = 155; + + public ImageSource CustomImage { get; set; } + + public Visibility ImageVisibility => (this.CustomImage is null) ? Visibility.Collapsed : Visibility.Visible; + + public double CancelButtonMinWidth { get; set; } = ButtonMinWidth; + + public double CancelButtonMaxWidth { get; set; } = ButtonMaxWidth; + + public double NoButtonMinWidth { get; set; } = ButtonMinWidth; + + public double NoButtonMaxWidth { get; set; } = ButtonMaxWidth; + + public double YesButtonMinWidth { get; set; } = ButtonMinWidth; + + public double YesButtonMaxWidth { get; set; } = ButtonMaxWidth; + + public double OkButtonMinWidth { get; set; } = ButtonMinWidth; + + public double OkButtonMaxWidth { get; set; } = ButtonMaxWidth; + + public Visibility CancelButtonVisibility { get; set; } = Visibility.Collapsed; + + public Visibility NoButtonVisibility { get; set; } = Visibility.Collapsed; + + public Visibility YesButtonVisibility { get; set; } = Visibility.Collapsed; + + public Visibility OkButtonVisibility { get; set; } = Visibility.Collapsed; + + public ButtonClickCommand CancelButtonClick { get; set; } + + public ButtonClickCommand NoButtonClick { get; set; } + + public ButtonClickCommand YesButtonClick { get; set; } + + public ButtonClickCommand OkButtonClick { get; set; } + + #endregion + + #region INotifyPropertyChanged + + public event PropertyChangedEventHandler PropertyChanged; + + public void OnPropertyChanged(string name) + { + this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + } + + #endregion + } +} diff --git a/source/WPFCustomMessageBox/CustomMessageBoxWindow.xaml b/source/WPFCustomMessageBox/CustomMessageBoxWindow.xaml deleted file mode 100644 index a6d7943..0000000 --- a/source/WPFCustomMessageBox/CustomMessageBoxWindow.xaml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/source/WPFCustomMessageBox/CustomMessageBoxWindow.xaml.cs b/source/WPFCustomMessageBox/CustomMessageBoxWindow.xaml.cs deleted file mode 100644 index 11badd4..0000000 --- a/source/WPFCustomMessageBox/CustomMessageBoxWindow.xaml.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System.Drawing; -using System.Windows; - -namespace WPFCustomMessageBox -{ - /// - /// Interaction logic for ModalDialog.xaml - /// - internal partial class CustomMessageBoxWindow : Window - { - #region Properties - - internal string Caption - { - get - { - return this.Title; - } - set - { - this.Title = value; - } - } - - internal string Message - { - get - { - return this.TextBlock_Message.Text; - } - set - { - this.TextBlock_Message.Text = value; - } - } - - internal string OkButtonText - { - get - { - return this.Label_Ok.Content.ToString(); - } - set - { - this.Label_Ok.Content = value.TryAddKeyboardAccellerator(); - } - } - - internal string CancelButtonText - { - get - { - return this.Label_Cancel.Content.ToString(); - } - set - { - this.Label_Cancel.Content = value.TryAddKeyboardAccellerator(); - } - } - - internal string YesButtonText - { - get - { - return this.Label_Yes.Content.ToString(); - } - set - { - this.Label_Yes.Content = value.TryAddKeyboardAccellerator(); - } - } - - internal string NoButtonText - { - get - { - return this.Label_No.Content.ToString(); - } - set - { - this.Label_No.Content = value.TryAddKeyboardAccellerator(); - } - } - - public MessageBoxResult Result { get; set; } - - #endregion - - #region Constructor - - internal CustomMessageBoxWindow(string message, string caption, MessageBoxButton button, MessageBoxImage image) - { - this.InitializeComponent(); - - this.Message = message; - this.Caption = caption; - this.Image_MessageBox.Visibility = Visibility.Collapsed; - - this.DisplayImage(image); - this.DisplayButtons(button); - } - - #endregion - - #region Methods - - private void DisplayButtons(MessageBoxButton button) - { - switch (button) - { - case MessageBoxButton.OKCancel: - // Hide all but OK, Cancel - this.Button_OK.Visibility = System.Windows.Visibility.Visible; - this.Button_OK.Focus(); - this.Button_Cancel.Visibility = System.Windows.Visibility.Visible; - - this.Button_Yes.Visibility = System.Windows.Visibility.Collapsed; - this.Button_No.Visibility = System.Windows.Visibility.Collapsed; - break; - - case MessageBoxButton.YesNo: - // Hide all but Yes, No - this.Button_Yes.Visibility = System.Windows.Visibility.Visible; - this.Button_Yes.Focus(); - this.Button_No.Visibility = System.Windows.Visibility.Visible; - - this.Button_OK.Visibility = System.Windows.Visibility.Collapsed; - this.Button_Cancel.Visibility = System.Windows.Visibility.Collapsed; - break; - - case MessageBoxButton.YesNoCancel: - // Hide only OK - this.Button_Yes.Visibility = System.Windows.Visibility.Visible; - this.Button_Yes.Focus(); - this.Button_No.Visibility = System.Windows.Visibility.Visible; - this.Button_Cancel.Visibility = System.Windows.Visibility.Visible; - - this.Button_OK.Visibility = System.Windows.Visibility.Collapsed; - break; - - default: - // Hide all but OK - this.Button_OK.Visibility = System.Windows.Visibility.Visible; - this.Button_OK.Focus(); - - this.Button_Yes.Visibility = System.Windows.Visibility.Collapsed; - this.Button_No.Visibility = System.Windows.Visibility.Collapsed; - this.Button_Cancel.Visibility = System.Windows.Visibility.Collapsed; - break; - } - } - - private void DisplayImage(MessageBoxImage image) - { - Icon icon; - - switch (image) - { - case MessageBoxImage.None: - return; - - case MessageBoxImage.Exclamation: // Enumeration value 48 - also covers "Warning" - icon = SystemIcons.Exclamation; - break; - - case MessageBoxImage.Error: // Enumeration value 16, also covers "Hand" and "Stop" - icon = SystemIcons.Hand; - break; - - case MessageBoxImage.Information: // Enumeration value 64 - also covers "Asterisk" - icon = SystemIcons.Information; - break; - - case MessageBoxImage.Question: - icon = SystemIcons.Question; - break; - - default: - icon = SystemIcons.Information; - break; - } - - this.Image_MessageBox.Source = icon.ToImageSource(); - this.Image_MessageBox.Visibility = Visibility.Visible; - } - - private void Button_OK_Click(object sender, RoutedEventArgs e) - { - this.Result = MessageBoxResult.OK; - this.Close(); - } - - private void Button_Cancel_Click(object sender, RoutedEventArgs e) - { - this.Result = MessageBoxResult.Cancel; - this.Close(); - } - - private void Button_Yes_Click(object sender, RoutedEventArgs e) - { - this.Result = MessageBoxResult.Yes; - this.Close(); - } - - private void Button_No_Click(object sender, RoutedEventArgs e) - { - this.Result = MessageBoxResult.No; - this.Close(); - } - - #endregion - } -} diff --git a/source/WPFCustomMessageBox/MessageBoxData.cs b/source/WPFCustomMessageBox/MessageBoxData.cs deleted file mode 100644 index f3e992e..0000000 --- a/source/WPFCustomMessageBox/MessageBoxData.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Threading; -using System.Windows; - -namespace WPFCustomMessageBox -{ - internal class MessageBoxData - { - #region Properties - - public Window Owner { get; set; } - - public string Message { get; set; } = ""; - - public string Caption { get; set; } = "Message"; - - public MessageBoxButton Buttons { get; set; } = MessageBoxButton.OK; - - public MessageBoxImage Image { get; set; } = MessageBoxImage.None; - - public string YesButtonCaption { get; set; } - - public string NoButtonCaption { get; set; } - - public string CancelButtonCaption { get; set; } - - public string OkButtonCaption { get; set; } - - public MessageBoxResult Result { get; set; } = MessageBoxResult.None; - - #endregion - - #region Methods - - /// - /// Displays a message box that is defined by the properties of this class. - /// In case the current thread is not a STA thread already, - /// a new STA thread is being created and the MessageBox is being displayed from there. - /// - /// - public MessageBoxResult ShowMessageBox() - { - if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) - { - this.ShowMessageBoxSTA(); - } - else - { - var thread = new Thread(this.ShowMessageBoxSTA); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - } - - return this.Result; - } - - private void ShowMessageBoxSTA() - { - var msg = new CustomMessageBoxWindow(this.Message, this.Caption, this.Buttons, this.Image); - - msg.YesButtonText = this.YesButtonCaption ?? msg.YesButtonText; - msg.NoButtonText = this.NoButtonCaption ?? msg.NoButtonText; - msg.CancelButtonText = this.CancelButtonCaption ?? msg.CancelButtonText; - msg.OkButtonText = this.OkButtonCaption ?? msg.OkButtonText; - msg.Owner = this.Owner ?? msg.Owner; - - msg.ShowDialog(); - - this.Result = msg.Result; - } - - #endregion - } -} diff --git a/source/WPFCustomMessageBox/MessageBoxModel.cs b/source/WPFCustomMessageBox/MessageBoxModel.cs new file mode 100644 index 0000000..eff0a98 --- /dev/null +++ b/source/WPFCustomMessageBox/MessageBoxModel.cs @@ -0,0 +1,415 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; + +namespace WPFCustomMessageBox +{ + /// + /// Model that can be used to configure and the show message boxes + /// + public class MessageBoxModel + { + #region Constants + + private static double ButtonMinWidth => 26; + + private static double ButtonMaxWidth => 2000; + + #endregion + + #region Properties + + /// + /// Maximum window height + /// + public double MaxHeight + { + get => this.ViewModel.MaxHeight; + set => this.ViewModel.MaxHeight = Math.Min(Math.Max(value, 155), 10000); + } + + /// + /// Window height + /// + public double Height + { + get => (this.ViewModel.MinHeight == this.ViewModel.MaxHeight) ? this.ViewModel.MinHeight : double.NaN; + set + { + this.MaxHeight = value; + this.ViewModel.MinHeight = this.MaxHeight; + } + } + + /// + /// Maximum window width + /// + public double MaxWidth + { + get => this.ViewModel.MaxWidth; + set => this.ViewModel.MaxWidth = Math.Min(Math.Max(value, 154), 10000); + } + + /// + /// Window width + /// + public double Width + { + get => (this.ViewModel.MinWidth == this.ViewModel.MaxWidth) ? this.ViewModel.MinWidth : double.NaN; + set + { + this.MaxWidth = value; + this.ViewModel.MinWidth = this.MaxWidth; + } + } + + /// + /// Maximum button width. + /// This overwrites custom button widths. + /// If both should be changed, please set the max button width first and then change the width of selected buttons. + /// + public double MaxButtonWidth + { + get + { + if ((this.ViewModel.CancelButtonMaxWidth == this.ViewModel.OkButtonMaxWidth) + && (this.ViewModel.NoButtonMaxWidth == this.ViewModel.OkButtonMaxWidth) + && (this.ViewModel.YesButtonMaxWidth == this.ViewModel.OkButtonMaxWidth)) + { + return this.ViewModel.OkButtonMaxWidth; + } + return double.NaN; + } + set + { + this.ViewModel.OkButtonMaxWidth = Math.Min(Math.Max(value, ButtonMinWidth), ButtonMaxWidth); + this.ViewModel.OkButtonMinWidth = Math.Min(this.ViewModel.OkButtonMinWidth, this.ViewModel.OkButtonMaxWidth); + this.ViewModel.CancelButtonMaxWidth = this.ViewModel.OkButtonMaxWidth; + this.ViewModel.CancelButtonMinWidth = Math.Min(this.ViewModel.CancelButtonMinWidth, this.ViewModel.OkButtonMaxWidth); + this.ViewModel.NoButtonMaxWidth = this.ViewModel.OkButtonMaxWidth; + this.ViewModel.NoButtonMinWidth = Math.Min(this.ViewModel.NoButtonMinWidth, this.ViewModel.OkButtonMaxWidth); + this.ViewModel.YesButtonMaxWidth = this.ViewModel.OkButtonMaxWidth; + this.ViewModel.YesButtonMinWidth = Math.Min(this.ViewModel.YesButtonMinWidth, this.ViewModel.OkButtonMaxWidth); + } + } + + /// + /// Minimum button width. + /// This overwrites custom button widths. + /// If both should be changed, please set the min button width first and then change the width of selected buttons. + /// + public double MinButtonWidth + { + get + { + if ((this.ViewModel.CancelButtonMinWidth == this.ViewModel.OkButtonMinWidth) + && (this.ViewModel.NoButtonMinWidth == this.ViewModel.OkButtonMinWidth) + && (this.ViewModel.YesButtonMinWidth == this.ViewModel.OkButtonMinWidth)) + { + return this.ViewModel.OkButtonMinWidth; + } + return double.NaN; + } + set + { + this.ViewModel.OkButtonMinWidth = Math.Min(Math.Max(value, ButtonMinWidth), ButtonMaxWidth); + this.ViewModel.OkButtonMaxWidth = Math.Max(this.ViewModel.OkButtonMaxWidth, this.ViewModel.OkButtonMinWidth); + this.ViewModel.CancelButtonMinWidth = this.ViewModel.OkButtonMinWidth; + this.ViewModel.CancelButtonMaxWidth = Math.Min(this.ViewModel.CancelButtonMaxWidth, this.ViewModel.OkButtonMinWidth); + this.ViewModel.NoButtonMinWidth = this.ViewModel.OkButtonMinWidth; + this.ViewModel.NoButtonMaxWidth = Math.Min(this.ViewModel.NoButtonMaxWidth, this.ViewModel.OkButtonMinWidth); + this.ViewModel.YesButtonMinWidth = this.ViewModel.OkButtonMinWidth; + this.ViewModel.YesButtonMaxWidth = Math.Min(this.ViewModel.YesButtonMaxWidth, this.ViewModel.OkButtonMinWidth); + } + } + + /// + /// Cancel button width (double.NaN for 'Auto') + /// + public double CancelButtonWidth + { + get => (this.ViewModel.CancelButtonMinWidth == this.ViewModel.CancelButtonMaxWidth) ? this.ViewModel.CancelButtonMinWidth : double.NaN; + set + { + this.ViewModel.CancelButtonMaxWidth = Math.Min(Math.Max(value, ButtonMinWidth), ButtonMaxWidth); + this.ViewModel.CancelButtonMinWidth = this.ViewModel.CancelButtonMaxWidth; + } + } + + /// + /// No button width (double.NaN for 'Auto') + /// + public double NoButtonWidth + { + get => (this.ViewModel.NoButtonMinWidth == this.ViewModel.NoButtonMaxWidth) ? this.ViewModel.NoButtonMinWidth : double.NaN; + set + { + this.ViewModel.NoButtonMaxWidth = Math.Min(Math.Max(value, ButtonMinWidth), ButtonMaxWidth); + this.ViewModel.NoButtonMinWidth = this.ViewModel.NoButtonMaxWidth; + } + } + + /// + /// Yes button width (double.NaN for 'Auto') + /// + public double YesButtonWidth + { + get => (this.ViewModel.YesButtonMinWidth == this.ViewModel.YesButtonMaxWidth) ? this.ViewModel.YesButtonMinWidth : double.NaN; + set + { + this.ViewModel.YesButtonMaxWidth = Math.Min(Math.Max(value, ButtonMinWidth), ButtonMaxWidth); + this.ViewModel.YesButtonMinWidth = this.ViewModel.YesButtonMaxWidth; + } + } + + /// + /// OK button width (double.NaN for 'Auto') + /// + public double OkButtonWidth + { + get => (this.ViewModel.OkButtonMinWidth == this.ViewModel.OkButtonMaxWidth) ? this.ViewModel.OkButtonMinWidth : double.NaN; + set + { + this.ViewModel.OkButtonMaxWidth = Math.Min(Math.Max(value, ButtonMinWidth), ButtonMaxWidth); + this.ViewModel.OkButtonMinWidth = this.ViewModel.OkButtonMaxWidth; + } + } + + /// + /// Owner window + /// + public Window Owner { get; set; } + + /// + /// Message to be displayed + /// + public string Message + { + get => this.ViewModel.Message; + set => this.ViewModel.Message = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Caption of the message box + /// + public string Caption + { + get => this.ViewModel.Caption; + set => this.ViewModel.Caption = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Buttons to show + /// + public MessageBoxButton Buttons { get; set; } = MessageBoxButton.OK; + + /// + /// Image that should be shown + /// + public MessageBoxImage Image { get; set; } = MessageBoxImage.None; + + /// + /// Custom image that should be shown. + /// Must be 32x32 pixels in size. + /// This property overwrites the @Image property if it is not null. + /// + public ImageSource CustomImage { get; set; } + + /// + /// Caption of the 'Cancel' button + /// + public string CancelButtonCaption + { + get => this.ViewModel.CancelButtonCaption.TryRemoveKeyboardAccellerator(); + set => this.ViewModel.CancelButtonCaption = value.TryAddKeyboardAccellerator(); + } + + /// + /// Caption of the 'No' button + /// + public string NoButtonCaption + { + get => this.ViewModel.NoButtonCaption.TryRemoveKeyboardAccellerator(); + set => this.ViewModel.NoButtonCaption = value.TryAddKeyboardAccellerator(); + } + + /// + /// Caption of the 'Yes' button + /// + public string YesButtonCaption + { + get => this.ViewModel.YesButtonCaption.TryRemoveKeyboardAccellerator(); + set => this.ViewModel.YesButtonCaption = value.TryAddKeyboardAccellerator(); + } + + /// + /// Caption of the 'OK' button + /// + public string OkButtonCaption + { + get => this.ViewModel.OkButtonCaption.TryRemoveKeyboardAccellerator(); + set => this.ViewModel.OkButtonCaption = value.TryAddKeyboardAccellerator(); + } + + /// + /// Result of the MessageBox + /// + public MessageBoxResult Result { get; private set; } = MessageBoxResult.None; + + private event Action RequestClosingEvent; + + private ManualResetEvent WindowClosedEvent { get; } = new ManualResetEvent(true); + + private CustomMessageBoxViewModel ViewModel { get; } + + private static Dictionary IconLookup { get; } = new Dictionary() + { + { MessageBoxImage.None, null }, + { MessageBoxImage.Error, SystemIcons.Hand }, // Hand, Stop and Error share the same value '16' + { MessageBoxImage.Question, SystemIcons.Question }, + { MessageBoxImage.Warning, SystemIcons.Warning }, // Exclamation and Warning share the same value '48' + { MessageBoxImage.Information, SystemIcons.Information } // Information and Asterisk share the same value '64' + }; + + #endregion + + #region Constructor + + /// + /// Constructor + /// + public MessageBoxModel() + { + this.ViewModel = new CustomMessageBoxViewModel() + { + CancelButtonClick = new ButtonClickCommand(this.SetResult, MessageBoxResult.Cancel), + NoButtonClick = new ButtonClickCommand(this.SetResult, MessageBoxResult.No), + YesButtonClick = new ButtonClickCommand(this.SetResult, MessageBoxResult.Yes), + OkButtonClick = new ButtonClickCommand(this.SetResult, MessageBoxResult.OK) + }; + } + + #endregion + + #region Methods + + /// + /// Displays a message box that is defined by the properties of this class. + /// In case the current thread is not a STA thread already, + /// a new STA thread is being created and the MessageBox is being displayed from there. + /// This method is blocking until user input. + /// + /// + public MessageBoxResult ShowDialog() + { + this.ConfigureViewModel(); + + if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) + { + this.ShowDialogSTA(); + } + else + { + var thread = new Thread(this.ShowDialogSTA); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + } + + return this.Result; + } + + /// + /// Displays a message box that is defined by the properties of this class. + /// In case the current thread is not a STA thread already, + /// a new STA thread is being created and the MessageBox is being displayed from there. + /// This method is not blocking. + /// + /// Task that will complete once the user closes the message box + public Task Show() + => Task.Factory.StartNew(() => this.ShowDialog()); + + /// + /// Closes the current message box if it is still open + /// + public void Close() + { + this.RequestClosingEvent.Invoke(); + this.WindowClosedEvent.WaitOne(); + } + + private void ShowDialogSTA() + { + var msg = new CustomMessageBoxView(); + msg.Owner = this.Owner; + msg.DataContext = this.ViewModel; + msg.Closed += this.MessageBoxClosed; + + // Handle focus (using non MVVM approach here because MVVM would be just far too complicated in this case) + if (this.ViewModel.OkButtonVisibility == Visibility.Visible) + { + msg.OkButton.Focus(); + } + else if (this.ViewModel.YesButtonVisibility == Visibility.Visible) + { + msg.YesButton.Focus(); + } + + this.RequestClosingEvent = null; + this.RequestClosingEvent += new Action(() => + { + msg?.Close(); + msg = null; + }); + + msg.ShowDialog(); + } + + private void ConfigureViewModel() + { + // Set image + this.ViewModel.CustomImage = this.CustomImage ?? MessageBoxModel.IconLookup[this.Image]?.ToImageSource(); + + // Set buttons + switch (this.Buttons) + { + case MessageBoxButton.OKCancel: + this.ViewModel.OkButtonVisibility = Visibility.Visible; + this.ViewModel.CancelButtonVisibility = Visibility.Visible; + break; + + case MessageBoxButton.YesNoCancel: + this.ViewModel.CancelButtonVisibility = Visibility.Visible; + this.ViewModel.NoButtonVisibility = Visibility.Visible; + this.ViewModel.YesButtonVisibility = Visibility.Visible; + break; + + case MessageBoxButton.YesNo: + this.ViewModel.NoButtonVisibility = Visibility.Visible; + this.ViewModel.YesButtonVisibility = Visibility.Visible; + break; + + default: //MessageBoxButton.OK + this.ViewModel.OkButtonVisibility = Visibility.Visible; + break; + } + } + + private void SetResult(MessageBoxResult result) + { + this.Result = result; + this.Close(); + } + + private void MessageBoxClosed(object sender, EventArgs e) + { + this.WindowClosedEvent.Set(); + } + + #endregion + } +} diff --git a/source/WPFCustomMessageBox/Properties/AssemblyInfo.cs b/source/WPFCustomMessageBox/Properties/AssemblyInfo.cs index 7e81b37..62108db 100644 --- a/source/WPFCustomMessageBox/Properties/AssemblyInfo.cs +++ b/source/WPFCustomMessageBox/Properties/AssemblyInfo.cs @@ -1,6 +1,4 @@ using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; diff --git a/source/WPFCustomMessageBox/Util.cs b/source/WPFCustomMessageBox/Util.cs index 0780199..d45faed 100644 --- a/source/WPFCustomMessageBox/Util.cs +++ b/source/WPFCustomMessageBox/Util.cs @@ -42,9 +42,24 @@ internal static string TryAddKeyboardAccellerator(this string input) const string accellerator = "_"; // This is the default WPF accellerator symbol - used to be & in WinForms // If it already contains an accellerator, do nothing - if (input.Contains(accellerator)) return input; + if (input.StartsWith(accellerator) == true) return input; return accellerator + input; } + + /// + /// Removes the acellerator added by TryAddKeyboardAccellerator() + /// + /// + /// + internal static string TryRemoveKeyboardAccellerator(this string input) + { + const string accellerator = "_"; // This is the default WPF accellerator symbol - used to be & in WinForms + + // If it already contains an accellerator, do nothing + if (input.StartsWith(accellerator) == false) return input; + + return input.Remove(0, 1); + } } } diff --git a/source/WPFCustomMessageBox/WPFCustomMessageBox.csproj b/source/WPFCustomMessageBox/WPFCustomMessageBox.csproj index 08c328d..9363030 100644 --- a/source/WPFCustomMessageBox/WPFCustomMessageBox.csproj +++ b/source/WPFCustomMessageBox/WPFCustomMessageBox.csproj @@ -16,25 +16,6 @@ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - @@ -45,11 +26,16 @@ AnyCPU bin\Debug\ bin\Debug\WPFCustomMessageBox.XML + DEBUG + true + CS0067 AnyCPU bin\Release\ bin\Release\WPFCustomMessageBox.XML + true + CS0067 @@ -64,13 +50,15 @@ - - CustomMessageBoxWindow.xaml + + CustomMessageBoxView.xaml - + + + - + Designer MSBuild:Compile