dotnet / winforms

Windows Forms is a .NET UI framework for building Windows desktop applications.
MIT License
4.41k stars 980 forks source link

[API Suggestion] Improve MessageBox in WinForms dotnet Core #12044

Open memoarfaa opened 1 month ago

memoarfaa commented 1 month ago

Background and motivation

Improving MessageBox is something every developer wants, so they all resort to creating an alternative interface for it. You can find many examples on Github. This is because they cannot localize it or change its appearance, so this new API solves this problem.

Untitledllll

Untitledlast

API Proposal

namespace System.Windows.Forms;

public class MessageBox
{
  /// <summary>
 ///  Type of <see cref="Resources"/> that used to localize Buttons in <see cref="System.Windows.Forms.MessageBox"/>.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static Type? ResourceType { get; set; }
 /// <summary>
 ///  The foreground color of the  <see cref="System.Windows.Forms.MessageBox"/>.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static Color ForeColor { get; set; } = SystemColors.ControlText;
 /// <summary>
 ///  The background color of the  <see cref="System.Windows.Forms.MessageBox"/>.
 /// </summary>

 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static Color BackColor { get; set; } = SystemColors.Window;
 /// <summary>
 ///  The background color of the Footer in <see cref="System.Windows.Forms.MessageBox"/>.
 /// </summary>

 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static Color FooterBackColor { get; set; } = SystemColors.Control;

    /// <summary>
    ///  Sets or gets the MessageBox BorderRadius.
    /// </summary>

    [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
    public MessageBoxCornerPreference MessageBoxBorderRadius{ get;set;}
    /// <summary>
    ///  Sets or gets the MessageBox's border color.
    /// </summary>

    public Color MessageBoxBorderColor{ get;set;}
    /// <summary>
    ///  Sets or gets the MessageBox's title bar back color (caption back color).
    /// </summary>
    [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
    public Color MessageBoxCaptionColor{ get;set;}

    /// <summary>
    ///  Sets or gets the MessageBox's title bar text color.
    /// </summary>
    [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
    public Color MessageBoxCaptionTextColor{ get;set;}
}

API Usage


MessageBox.ResourceType = typeof(Properties.Resources)
MessageBox.ForeColor = SystemColors.ControlText;
MessageBox.BackColor = SystemColors.Window;
MessageBox.FooterBackColor = SystemColors.Control;

 MessageBox.Show("MessageBox with Custom Icon", "Custom Icon", MessageBoxButtons.YesNo, SystemIcons.GetStockIcon(StockIconId.Users, StockIconOptions.ShellIconSize));

Alternative Designs

 /// <summary>
 ///  Displays a message box with specified text, caption, style, and custom Icon. with Help Button.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     bool displayHelpButton)
 {
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, displayHelpButton);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path, and custom Icon.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath)
 {
     HelpInfo hpi = new(helpFilePath);
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path, and custom Icon. for a IWin32Window.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     IWin32Window? owner,
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath)
 {
     HelpInfo hpi = new(helpFilePath);
     s_customIcon = icon;
     return ShowCore(owner, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path, keyword, and custom Icon.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath,
     string keyword)
 {
     HelpInfo hpi = new(helpFilePath, keyword);
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path, keyword, and custom Icon for a IWin32Window.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     IWin32Window? owner,
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath,
     string keyword)
 {
     HelpInfo hpi = new(helpFilePath, keyword);
     s_customIcon = icon;
     return ShowCore(owner, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path HelpNavigator, and custom Icon.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath,
     HelpNavigator navigator)
 {
     HelpInfo hpi = new(helpFilePath, navigator);
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path HelpNavigator, and custom Icon for IWin32Window.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     IWin32Window? owner,
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath,
     HelpNavigator navigator)
 {
     HelpInfo hpi = new(helpFilePath, navigator);
     s_customIcon = icon;
     return ShowCore(owner, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path ,HelpNavigator,object and custom Icon.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath,
     HelpNavigator navigator,
     object? param)
 {
     HelpInfo hpi = new(helpFilePath, navigator, param);
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, Help file Path ,HelpNavigator, object and custom Icon for a IWin32Window.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     IWin32Window? owner,
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options,
     string helpFilePath,
     HelpNavigator navigator,
     object? param)
 {
     HelpInfo hpi = new(helpFilePath, navigator, param);
     s_customIcon = icon;
     return ShowCore(owner, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, hpi);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, and custom Icon.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton,
     MessageBoxOptions options)
 {
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, defaultButton, options, false);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, and custom Icon.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
     string? text,
     string? caption,
     MessageBoxButtons buttons,
     Icon icon,
     MessageBoxDefaultButton defaultButton)
 {
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, defaultButton, 0, false);
 }

 /// <summary>
 ///  Displays a message box with specified text, caption, style, and custom Icon.
 /// </summary>
 [Experimental(DiagnosticIDs.ExperimentalMessageBox, UrlFormat = DiagnosticIDs.UrlFormat)]
 public static DialogResult Show(
   string? text,
   string? caption,
   MessageBoxButtons buttons,
   Icon icon)
 {
     s_customIcon = icon;
     return ShowCore(null, text, caption, buttons, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, 0, false);
 }

Risks

No response

Will this feature affect UI controls?

Will VS Designer need to support the feature? no Will this feature need to be localized or be localizable? yes

merriemcgaw commented 1 month ago

@memoarfaa in .NET 5 we added support for the OS TaskDialog which allows for lots of customization. I think that's the direction we would rather developers go.

@Olina-Zhang @LeafShi1 can you verify for me that TaskDialog API is able to respond to OS dark mode?

memoarfaa commented 1 month ago

@memoarfaa in .NET 5 we added support for the OS TaskDialog which allows for lots of customization.

@merriemcgaw Yes that is true but each has its own use If I need a lot of customization I use TaskDialog otherwise I use MessageBox 1- Can I use TaskDialog to display a simple message withowt extra coding. For example, I want to display a simple message to tell the user that the database login failed.

 private void CheckConnectionStatus()
 {
     try
     {
         if (_sqlConnection.State != ConnectionState.Open)
         {
             _sqlConnection.Open();
         }

     }
     catch (Exception ex)
     {
         MessageBox.Show(ex.ToString(), "Failed to Open Connection");
     }
 }

This is not about OS dark mode only. But the question is Winforms has a way to show simple message that can be localized and color without extra coding?

I saw a similar issue still open in Wpf. https://github.com/dotnet/wpf/issues/5967 Most opinions say that Messagebox in winForms is better than Wpf because it has manifest. In fact they don't need manifest or CreateActCtx to make Messagebox better.

Why don't we make Messagebox much better?

About TaskDialog @Olina-Zhang @LeafShi1 can you verify for that we can change the background and foreground color ?

Is there a problem, if both Messagebox and TaskDialog have a more flexible look?

I will open a pull request for doing this if the team agrees otherwise I will close this issue as not acceptable api.

Olina-Zhang commented 1 month ago

@Olina-Zhang @LeafShi1 can you verify for me that TaskDialog API is able to respond to OS dark mode?

Setting OS with dark mode color cannot respond to a series of TaskDialogs, although enabled DarkMode for application by Application.SetColorMode(SystemColorMode.Dark)

https://github.com/user-attachments/assets/c7b1b23c-004e-411b-bd41-8413bb35573b

merriemcgaw commented 1 month ago

The team's consensus is that we try to minimize touching the MessageBox as much as possible. That said, we believe that DarkMode support would be neat for it. Just not actual styling - that should go to the TaskDialog.

@Olina-Zhang can you file a separate issue about TaskDialog not responding to DarkMode?

Olina-Zhang commented 1 month ago

Filed an issue forTaskDialog not responding to DarkMode: https://github.com/dotnet/winforms/issues/12062.