65.9K
CodeProject 正在变化。 阅读更多。
Home

可自定义的 WPF 消息框

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (55投票s)

2020年12月25日

CPOL

29分钟阅读

viewsIcon

106983

downloadIcon

2915

终于,一个可定制的 WPF 消息框

引言

我老了。真的老了。随着年龄的增长(或者也许只是因为我年轻时做出了错误的决定),我的视力开始衰退。一切都变得模糊不清,我找不到一个合适的距离来拿书,如果不是那么烦人,我可能会觉得万花筒般的彩色光晕围绕着一切有点漂亮。

与视力衰退相关的特别烦人的地方是,除非您以10%的增量增加字体大小,否则Windows使用的文本微乎其微。问题在于,即使是110%,文本也比实际需要的大得多。无论如何,这种微小的文本被用于标准的WPF消息框中,并且无法更改。事实上,标准的WPF消息框没有任何东西可以更改。这种认识最终让我无法忍受,我被迫编写了这段代码。

但首先,抱怨

当我开始研究标准的 WPF 消息框时,我惊讶地发现它不是一个 WPF 特定的对象。事实证明,它完全由 Win32.Interop 功能实现,并且没有一丁点 WPF 的优点。我猜一个微软的辩护者可能会试图说明他们的方法如何通过几千字节来减少整体 .Net 代码占用,但我不买账。这里有一个真正的机会,但微软浪费了它。顺便说一句,这就是它无法以任何方式配置的原因——它使用 Windows 系统属性,仅此而已。您不允许偏离这些参数。糟糕的程序员!糟糕!糟糕!

我想实现什么

我的主要目标是开发一个可合理配置、完全基于WPF的消息框替代品,同时提供与原始消息框尽可能接近的界面。通过一些static的魔法,我能够轻松实现这一点。

模拟标准 WPF 消息框功能

我的首要任务是模拟标准的 WPF MessageBox。最初(在代码的先前迭代中),我为每个可能的参数组合提供了 Show() 方法的重载,但重载的数量变得难以维持。真正让我崩溃的是提供了指定默认按钮的功能——这简直是画蛇添足,所以我决定这样做。

MessageBoxEx.Show(string msg, params object[] parameters)

这允许用户指定消息文本和任何其他所需的参数。是的,使用此策略允许开发人员指定多个参数,但该方法对愚蠢的开发人员技巧有一些保护。该方法以设置为合理默认值的变量开始。在处理参数时,给定变量会一直设置,直到其值不再等于默认值。诚然,这本质上是相当小的,并且如果给定变量设置多次,可能会达到抛出异常的极端,但我宁愿不中断处理,除非发生了真正灾难性的事情,并且允许程序员犯蠢并不是灾难,而更多的是对当今已经宽松的道德标准的弹性的评论。

简而言之,如果你想犯傻,那就犯傻吧,因为这个方法会使用它能用的,而把剩下的扔掉。超出预期参数的任何操作都会导致处理时间的浪费(和过度),这已被证明会对本已脆弱的时空结构产生不利影响。这种方法真正的益处对我来说(这确实是唯一重要的),就是只有一个 Show() 方法需要维护,代码变得**简单得多**。这个方法可以可选地接受标题、所需按钮、图标图像和默认按钮,并且这些参数可以按任何顺序指定。任何其他参数都不受支持,并将被忽略。

标准的 WPF MessageBox(和 MessageBoxEx)接受以下对象

  • MessageBoxButton.OK
  • MessageBoxButton.OKCancel
  • MessageBoxButton.YesNo
  • MessageBoxButton.YesNoCancel
  • MessageBoxImage.Information(也可以使用 MessageBoxImage.Asterisk
  • MessageBoxImage.Question
  • MessageBoxImage.Warning(也可以使用 MessageBoxImage.Exclamation
  • MessageBoxImage.Error(也可以使用 MessageBoxImage.HandMessageBoxImage.Stop

注意 - 如果您指定了图标,则还会播放与该图标关联的系统声音。但是,您可以选择通过配置方法关闭声音播放。

虽然 WPF MessageBox 使用 MessageBoxDefaultResult 枚举器来指定默认按钮,但其范围相当有限,因此 MessageBoxEx 提供了一个新的 MessageBoxButtonDefault 枚举器,扩展了对默认按钮的支持。您可以指定一个特定按钮、按钮从左到右的序数位置、Windows.Forms.MessageBox 使用的默认值、指定按钮中最/最不积极的按钮,甚至完全不指定默认值。

新增功能

除了标准功能,我还增加了对以下项目的修改支持。这首先是编写此代码的主要原因。

  • 字体族
  • 字体大小
  • 消息区域文本和背景颜色
  • 按钮面板背景颜色
  • 自定义按钮模板支持
  • 增强的默认按钮支持
  • 设置消息框的最大可能宽度

值得注意的缺失功能

如果你曾为 Windows.Forms 编写过代码,你可能曾需要使用 Windows.Forms.MessageBox,你可能已经注意到微软决定不支持 MessageBoxButton.AbortRetryIgnoreMessageBoxButton.RetryCancel。这也意味着这些按钮在默认按钮设置中也不可用。MessageBoxEx 将这些按钮(和相关的默认值)重新添加回了使用范围——请参阅下一篇文章部分。太棒了!

扩展功能

在创建 MessageBoxEx 类时,我决定添加标准 WPF 消息框中没有的功能。这包括前面提到的缺失按钮、一个复选框、一个可点击的 URL、一个可点击的图标(用于执行外部代码)以及更多默认按钮选项。为了区分标准功能和扩展功能,您可以使用 MessageBoxEx.ShowEx() 方法,该方法增加了对 MessageBoxButtonEx 枚举器的支持,以及 MsgBoxExtendedFunctionality 类,该类允许您添加复选框、URL 和可点击图标。

以下按钮可用

  • MessageBoxButtonEx.OK
  • MessageBoxButtonEx.OKCancel
  • MessageBoxButtonEx.YesNo
  • MessageBoxButtonEx.YesNoCancel
  • MessageBoxButtonEx.AbortRetryIgnore
  • MessageBoxButtonEx.RetryCancel

还实现了以下扩展功能

  • 可点击图标 - 指定后,图标变为可点击,并允许您执行我称之为“错误委托”对象中定义的操作。这允许消息框在单击图标时执行某些操作。此功能适用于所有图像图标。当图标可点击时,光标将变为手形,并显示相关工具提示(如果指定)。此功能想法是由 CodeProject 休息室中关于生成屏幕截图以通过电子邮件发送给支持团队的讨论激发的。
     
  • 详细信息 - 消息框可以同时显示消息和“详细信息”。当您希望显示 Exception.Message 以及 Exception.StackTrace,但又不希望消息框过大时,这会很有用。详细信息面板是一个扩展器,当消息框显示时,它最初是折叠的。
     
  • 复选框 - 这会显示一个带有您指定的提示(内容)的 CheckBox。这的一些可能用途是在某些情况下抑制消息框的显示,或影响应用程序中的其他功能。如何处理复选框的值取决于开发人员。消息框除了显示它之外不关心。有关此功能如何使用的示例,请参阅示例应用程序中的 BtnClickme3c_Click() 方法。
     
  • 可点击 URL - 可点击 URL 大致相同。底层控件是 TextBlock,不支持 Click 事件,所以我不得不再次处理 MouseLeftButtonUp 事件。我还创建了一个自定义样式,在 MouseEnter 上触发,以便我可以将光标更改为手形。事后看来,我本可以S使用 RichTextBox 来显示 URL,但我认为为时已晚,而且,我不认为这样做值得费力。
     
  • 中止/重试/忽略和重试/取消按钮 - WPF MessageBox 不支持 Windows 窗体按钮 AbortRetryIgnoreRetryCancelMessageBoxEx 让您有机会将它们重新添加回来(通过扩展功能对象)。微软会这样半途而废,这让我感到困惑。
     
  • 默认按钮 - 您可以通过多种方式将任何按钮指定为默认按钮 - 特定按钮、根据其从左到右的序数位置的按钮、标准 Windows 窗体默认值(取决于使用的按钮)、最/最不积极的按钮,或完全不指定。

注意: 复选框和 URL 不包含在可滚动的消息区域中。这使得它们始终可见。

代码

实现此消息框所需的代码量出乎意料地少。少到我只用了几个小时就实现了它,然后将其移到本文的示例项目中。

注意:本文的原始版本包含几个代码转储,但由于更改频繁,试图让它们与代码中实际发生的情况保持同步变得很繁琐,因此我将它们从文章中删除了。如果您想查看我做了什么,可以浏览 CodeProject 上的代码,或者直接下载 ZIP 文件并查看(这更有趣)。

基本设计目标

如您所知,标准的 MessageBox 是一个不需要实例化即可使用的静态对象。这个新的 MessageBoxEx 也是静态的,或者至少是“静态的”。为了实现它,我创建了一个标准的 WPF 窗口,其中包含静态和非静态属性和方法。其思想是程序员将调用一个静态的 MessageBoxEx.Show...() 方法,该方法将实例化该窗口的非静态版本。我还希望外部体验与标准的 MessageBox 尽可能相同,因此返回值与您从标准消息框获得的返回值相同。

实现 - XAML

此表单的 XAML 相当有限,因为我们并没有做任何花哨的事情。我认为 XAML 中的注释提供了足够的文档,不需要伴随的叙述。

可点击图标 - 由于图像不可“点击”,我不得不求助于处理 MouseLeftButtonUp 事件。最重要的是,我希望当鼠标悬停在图标上时,图标的视觉外观会发生变化,所以我将默认不透明度设置为 0.85,当消息框显示错误时,我会在图像中添加一个自定义样式(在代码隐藏中)来响应鼠标悬停,这将不透明度更改为 1.0 并将光标更改为手形。我还使用代码隐藏将工具提示添加到图像(实际上是图像的父网格,因为我无法使绑定工具提示在图像元素上工作),因此用户可以得到某种指示,表明该图标确实是可点击的。

可点击 URL - 可点击 URL 大致相同。底层控件是 TextBlock,不支持 Click 事件,所以我不得不再次处理 MouseLeftButtonUp 事件。我还创建了一个自定义样式,在 MouseEnter 上触发,以便我可以将光标更改为手形。事后看来,我本可以S使用 RichTextBox 来显示 URL,但我认为为时已晚,而且,我不认为这样做值得费力。

实现 - C#

粗略检查后,代码背后并没有什么特别或技术上难以理解的地方。如前所述,代码主要分为静态和非静态字段、属性和方法。消息框本身需要实例化,但该类具有可用于轻松配置和渲染消息框的静态属性和方法。就像 MessageBox 一样,MessageBoxEx 提供了 Show() 方法(及其变体)的多个重载来显示自身。

就代码组织而言,我做了一些我通常不会做的事情,但这样做是为了方便将文件复制到您自己的代码中。首先,所有与 MessageBoxEx 类相关的类都位于 MessageBoxEx.xaml.cs 文件中。其次,我利用了该类是一个部分类的事实,将静态部分与非静态部分分开了。这可能看起来很奇怪,但它对我有所帮助,而这是最重要的事情。

为了使表单大小适中,我利用了窗口的 SizeToContent 属性(在 XAML 中)。此属性允许窗口根据其内容进行增长/缩小,从而使窗口仅在 XAML 中指定的最小和最大宽度/高度限制下尽可能大。SizeToContent 属性的一个奇怪的副作用是它在窗口在屏幕上定位之后才生效。这意味着如果窗口需要显示多行消息,则窗口将不会像预期那样在屏幕上“居中”。为了缓解这种行为,我必须处理窗口的 SizeChanged 事件,并手动将表单重新居中到屏幕上。代码足够简单,但我想在这里引用一下。

扩展功能

1月8日 - 请参阅本节末尾的更新通知!

扩展功能需要不同的方法名称(和必要的重载),因为这会导致参数冲突。除此之外,还需要一些额外的类来使实现和使用尽可能无缝。下面描述的所有扩展功能都可以组合到单个消息框中。

CheckBox

复选框允许您在消息框上显示一个复选框。复选框的内容由您指定,并且可以是您认为合适的任何内容。此功能要求您实例化一个 MsgBoxExCheckBoxData 对象,以便同时指定复选框内容并提供属性来确定复选框的“选中”状态。MsgBoxExCheckBoxData 类可以在 MessageBoxEx.xaml.cs 的底部找到。它看起来像这样

/// <summary>
/// Reresents the object that allows the checkbox state to be discoevered externally of the 
/// messagebox.
/// </summary>
public class MsgBoxExCheckBoxData : INotifyPropertyChanged
{
#region INotifyPropertyChanged

private bool isModified = false;
public bool IsModified { get { return this.isModified; } set { if (value != this.isModified) { this.isModified = true; this.NotifyPropertyChanged(); } } }
public event PropertyChangedEventHandler PropertyChanged;

protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        if (propertyName != "IsModified")
        {
            this.IsModified = true;
        }
    }
}
    
#endregion INotifyPropertyChanged

private string checkBoxText;
private bool   checkBoxIsChecked;

/// <summary>
/// Get/set the text content of the checkbox
/// </summary>
public string CheckBoxText      
{ 
    get { return this.checkBoxText;} 
    set 
    { 
        if (value != this.checkBoxText) 
        { 
            this.checkBoxText = value; 
            this.NotifyPropertyChanged(); 
        } 
    } 
}

/// <summary>
/// Get/set the flag that indicates whether the checkbox is checked
/// </summary>
public bool CheckBoxIsChecked 
{ 
    get { return this.checkBoxIsChecked; } 
    set 
    { 
        if (value != this.checkBoxIsChecked) 
        { 
            this.checkBoxIsChecked = value; 
            this.NotifyPropertyChanged(); 
        } 
    } 
}
}

要在您自己的代码中实现此功能,您需要实例化复选框数据对象。下面显示的代码是从示例应用程序中提取的。

MsgBoxExCheckBoxData checkboxData = new MsgBoxExCheckBoxData()
{ 
// I suspect you're sometimes going to want to persist this value (app 
// settings, etc), so how you set this value is completely up to you.
CheckBoxIsChecked = false, 
CheckBoxText = "Don't show this message anymore"
};

一旦您的复选框数据对象被实例化,您需要执行以下操作。

// In this particular example, handling the decision to show/not show a messagebox is 
// handled by checking the status of the checkbox data. The mesagebox itself doesn't care.
if (this.checkboxData != null && !this.checkboxData.CheckBoxIsChecked)
{
// show box with a checkbox
MessageBoxEx.ShowWithCheckBox("This is possibly pointless and can be permenantly "+
                                "dismissed by clicking the checkbox below."
                                , this.checkboxData, "Checkbox Sample");
}
else
{
MessageBoxEx.Show("But you said not to show the message anymore. Make up your mind.","Hypocrite Notice");
}

本质上,消息框不关心复选框的用途,开发人员有责任处理与复选框预期功能相关的所有持久性问题,以及数据对象实现的范围。在上面显示的示例中,数据对象在 MainWindow 类中声明,以便后续显示该消息框的尝试可以通过 CheckBoxIsChecked 属性的最后一个值来控制。

详细说明

此功能允许您在较短的消息框中显示大量文本,并且有助于将辅助信息与实际消息分开。例如,将 Exception.Message 指定为消息文本,然后将 Exception.StackTrace 指定为“详细信息”。使用此功能需要您使用 ShowWithDetails 方法的重载之一。

MessageBoxEx.ShowWithDetails("Here's some source code. Click the Details expander below."
                            , Testmsg()
                            , "Really Long Message, With Deatils"
                            , MessageBoxButton.OK
                            , MessageBoxImage.Information);

Testmsg() 方法返回一个长段源代码,作为详细信息窗格的内容。

可点击错误图标

此功能允许您使图标可点击,表面上是为了执行一些辅助操作。它仅限于错误图标,因为我认为那确实是您唯一需要此类功能的地方。我设想它用于向您的支持团队发送电子邮件,或者可能用于纠正可能以某种方式对执行产生不利影响的某些设置。无论如何,如果您希望所有图标都可用,修改代码使其实现应该相当简单。

您需要做的第一件事是创建一个继承自 MsgBoxExDelegate 的类。该类提供了响应点击图标所需的一切,但要求您重写 PerformAction 方法。

public abstract class MsgBoxExDelegate
{
/// <summary>
/// Get/set the message text from the calling message box
/// </summary>
public string   Message     { get; set; }
/// <summary>
/// Get/set the details text (if it was specified in the messagebox)
/// </summary>
public string   Details     { get; set; }
/// <summary>
/// Get/set the message datetime at which this object was created
/// </summary>
public DateTime MessageDate { get; set; }

/// <summary>
/// Performs the desired action, and returns the result. MUST BE OVERIDDEN IN INHERITING CLASS. 
/// </summary>
/// <returns></returns>
public virtual MessageBoxResult PerformAction(string message, string details=null)
{ 
    throw new NotImplementedException(); 
}
}

一个继承类可能看起来像这样

public class ErrorMsgDelegate : MsgBoxExDelegate
{
public override MessageBoxResult PerformAction(string message, string details=null)
{
    this.Message = message;
    this.Details = details;
    this.MessageDate = DateTime.Now;

    // for this sample, we're just showing another messagebox
    MessageBoxResult result = MessageBoxEx.Show("You're about to do something because this is an "+
                                                "error message (clicking yes with play a beep sound). "+
                                                "Are you sure?", 
                                                "Send Error Message To Support", 
                                                MessageBoxButton.YesNo, 
                                                MessageBoxImage.Question);
    if (result == MessageBoxResult.Yes)
    {
        //indicate that they clicked yes
        SystemSounds.Beep.Play();
    }
    return result;
}
}

PerformAction 方法中包含的代码量完全取决于您应用程序其余部分的架构。上面的示例仅在示例应用程序的上下文中行使该功能。

当消息框关闭时,消息框中对委托对象的引用被设置为 null,以避免在后续调用消息框时无意中触发可点击图标。

要使用此功能,请执行以下操作

MessageBoxEx.SetErrorDelegate(this.errorDelegate);

// if you want the message box to be closed when the action is completed, do this:
MessageBoxEx.SetExitAfterErrorAction(true);

MessageBoxEx.Show("Show an error message. The icon is clickable.", "Error"
                , MessageBoxButton.OK, MessageBoxImage.Error);

写完文章的这一部分后,我想到我可以将这两个“Set...”方法合并为一个,因为它们是关联的。好吧,即使对于经验不足的开发人员来说,更改起来也足够容易,所以如果你觉得有必要,就去做吧。

1月8日更新!

在实现新的 URL 代码时,很明显扩展参数变得难以控制(URL 是第四个添加的扩展功能),所以我创建了一个额外的对象,作为参数传递给 ShowExt 方法(连同消息、标题、按钮和图像)。这个对象叫做 MsgBoxExtendedFunctionality,你可以在其中放置一个或多个你想要在给定消息框中使用的扩展功能。这有几个目的:a) 减少“特殊”版本的 Show 方法的数量(每个都有八个重载),以及 b) 如果我想添加更多扩展功能,可以减少维护时间。旧的方法(ShowWithCheckBoxShowWithDetailsShowWithCheBoxAndDetails)仍然存在,但它们已被修改为调用新的 ShowExt 方法,并且 Intellisense 已更新以显示它们已弃用。

要查看其用法示例,请查找 ClickMe_Nc 方法(它们处理示例应用程序窗口右侧按钮的点击)。

用法

在您的应用程序启动的某个地方(我建议在 MainWindow 的构造函数中使用,因为那时您可以指定要继承适当字体属性的父窗口。在示例应用程序中,我创建了一个名为 InitMessageBox() 的方法,其中包含以下语句。

MessageBoxEx.SetParentWindow(this);
MessageBoxEx.SetMessageForeground(Colors.White);
MessageBoxEx.SetMessageBackground(Colors.Black);
MessageBoxEx.SetButtonBackground(MessageBoxEx.ColorFromString("#333333"));
MessageBoxEx.SetButtonTemplateName("AefCustomButton");
MessageBoxEx.SetMaxFormWidth(500);
		

在应用程序中完成此操作一次后,使用 MessageBoxEx 与使用原始 MessageBox 完全相同。

MessageBoxResult result = MessageBoxEx.Show("Your message. Continue?", "Caption", MessageBoxButton.YesNoCancel);
		

当然,如果未指定按钮组,则按钮规范默认为 MessageBoxButton.OK。

示例应用程序

MainWindow

示例应用程序本身很简单,提供了一系列按钮,旨在演示消息框的某些方面。以下屏幕截图与主窗口上的按钮并不完全对应,但它们确实说明了消息长度、图标和按钮的几种不同组合,并说明了在各种条件下图标的对齐方式。您应该使用示例应用程序来获得更实时的体验。每个屏幕截图前面都带有标准的 MessageBox 版本。

Small message (standard MessageBox)
Small message (MessageBoxEx)

Medium message (standard MessageBox)
Medium message (MessageBoxEx)

Longer message (standard MessageBox)
Longer message (MessageBoxEx)

Huge message (standard MessageBox)
Huge message (MessageBoxEx)

details collapsed
details expanded

URL example

如果你没有听到声音,那是因为你没有在 Windows 中指定关联的系统声音。这很可能发生在“问题”按钮上(我在那里遇到过,设置声音后,标准消息框和扩展消息框都播放了适当的声音)。

MessageBoxEx 类文档

以下是该类的逐方法描述。由于所有配置都通过静态方法处理,因此我将在这里讨论这些方法。几乎不会有任何代码转储。

Show...() 方法

Show() 方法显示一个消息框,它代表标准 WPF MessageBox 的功能类似版本。此方法接受以下参数。

string msg 这是要显示的消息文本。所有所需的格式都必须在调用方法中执行。至少,必须指定此参数。
params object[] parameters 此参数允许开发人员添加适用于给定消息框实例的任何参数。这样做是为了消除原本需要数十个 Show 方法重载。此方法将评估以下参数。
  • string - 评估为消息框窗口的预期标题。如果未指定,则窗口标题由图标(如果指定)或默认标题决定。
     
  • MessageBoxButton - 评估为要显示的预期按钮。如果未指定,则使用 MessageBoxButton.OK 的默认值。以下按钮可用
    • 好的
    • 确定取消
    • 是/否
    • 是/否/取消
  • MessageBoxImage - 评估为要显示的预期图标。如果未指定,则使用 MessageBoxImage.None 的默认值。以下图像可用
    • 信息星号的相同图标)
    • 提问
    • 警告感叹号的相同图标)
    • 错误停止的相同图标)
  • MessageBoxButtonDefault - 评估为预期的默认按钮。如果未指定,则使用 MessageBoxButtonDefault.Forms 的默认值(使用与 Windows.Forms.MessageBox 相同的默认按钮策略)。以下默认值可用
    • OK - 特定按钮
    • 取消 - 特定按钮
    • - 特定按钮
    • - 特定按钮
    • Button1 - 按从左到右的序数位置的第一个按钮
    • Button2 - 按从左到右的序数位置的第二个按钮
    • Button3 - 按从左到右的序数位置的第三个按钮
    • Forms - 与 Windows.Forms.MessageBox 默认值相同
    • MostPositive - 表示最积极结果的按钮
    • LeastPositive - 表示最不积极结果的按钮
    • None - 无默认按钮
  • Window - 评估为所有者窗口。如果未指定,将使用应用程序主窗口。
     

虽然可以指定多个参数,但 Show 方法在遇到不使用给定参数类型默认值的参数后将停止评估参数。例如,如果您的参数列表如下所示:

MessageBoxEx.Show("我的消息文本", MessageBoxButton.YesNo, MessageBoxButton.OK);

...消息框将显示“是/否”按钮,而“确定”按钮将被忽略。反之,如果您的参数列表如下所示:

MessageBoxEx.Show("我的消息文本", MessageBoxButton.OK, MessageBoxButton.YesNo);

...消息框仍将显示“是/否”按钮,因为“确定”按钮是默认值。最后,如果您这样做:

MessageBoxEx.Show("我的消息文本", MessageBoxButton.OkayCancel, MessageBoxButton.YesNo);

...消息框将显示“确定/取消”按钮,因为该值是遇到的第一个非默认值。

我当然无法控制你做什么,但指定同一参数的多个实例是毫无意义且愚蠢的,它只会迫使 Show 方法处理无用的参数,并花费更多时间来决定该做什么。

Show() 方法返回一个 MessageBoxResult 值。这用于指示单击了哪个按钮来关闭消息框窗口。如果用户单击窗口的关闭按钮,则返回值将根据以下内容选择:

  • 对于 MessageBoxButton.OK,结果将是 MessageBoxButton.OK
  • 对于 MessageBoxButton.OKCancel,结果将是 MessageBoxButton.Cancel
  • 对于 MessageBoxButton.YesNo,结果将是 MessageBoxButton.No
  • 对于 MessageBoxButton.YesNoCancel,结果将是 MessageBoxButton.Cancel

ShowEx() 方法与 Show() 方法类似,不同之处在于它显示一个实现扩展功能的消息框。此方法接受以下参数。

string msg 这是要显示的消息文本。所有所需的格式都必须在调用方法中执行。至少,必须指定此参数。
params object[] parameters 此参数允许开发人员添加适用于给定消息框实例的任何参数。这样做是为了消除原本需要数十个 Show 方法重载。此方法将评估以下参数。
  • string - 评估为消息框窗口的预期标题。如果未指定,则窗口标题由图标(如果指定)或默认标题决定。
     
  • MessageBoxButtonEx - 评估为要显示的预期按钮。如果未指定,则使用 MessageBoxButtonEx.OK 的默认值。以下按钮可用
    • 好的
    • 确定取消
    • 是/否
    • 是/否/取消
    • 中止重试忽略
    • 重试取消
  • MessageBoxImage - 评估为要显示的预期图标。如果未指定,则使用 MessageBoxImage.None 的默认值。以下图像可用
    • 信息星号的相同图标)
    • 提问
    • 警告感叹号的相同图标)
    • 错误停止的相同图标)
  • MessageBoxButtonDefault - 评估为预期的默认按钮。如果未指定,则使用 MessageBoxButtonDefault.Forms 的默认值(使用与 Windows.Forms.MessageBox 相同的默认按钮策略)。以下默认值可用
    • OK - 特定按钮
    • 取消 - 特定按钮
    • - 特定按钮
    • - 特定按钮
    • 中止 - 特定按钮
    • 重试 - 特定按钮
    • 忽略 - 特定按钮
    • Button1 - 按从左到右的序数位置的第一个按钮
    • Button2 - 按从左到右的序数位置的第二个按钮
    • Button3 - 按从左到右的序数位置的第三个按钮
    • Forms - 与 Windows.Forms.MessageBox 默认值相同
    • MostPositive - 表示最积极结果的按钮
    • LeastPositive - 表示最不积极结果的按钮
    • None - 无默认按钮
  • Window - 评估为所有者窗口。如果未指定,将使用应用程序主窗口。
     

虽然可以指定多个参数,但 Show 方法在遇到不使用给定参数类型默认值的参数后将停止评估参数。例如,如果您的参数列表如下所示:

MessageBoxEx.Show("My message text", MessageBoxButtonEx.YesNo, MessageBoxButtonEx.OK)

...消息框将显示“是/否”按钮,而“确定”按钮将被忽略。反之,如果您的参数列表如下所示:

MessageBoxEx.Show("My message text", MessageBoxButtonEx.OK, MessageBoxButtonEx.YesNo)

...消息框仍将显示“是/否”按钮,因为“确定”按钮是默认值。最后,如果您这样做:

MessageBoxEx.Show("My message text", MessageBoxButtonEx.OkayCancel, MessageBoxButtonEx.YesNo)

...消息框将显示“确定/取消”按钮,因为该值是遇到的第一个非默认值。

我当然无法控制你做什么,但指定同一参数的多个实例是毫无意义且愚蠢的,它只会迫使 Show 方法处理无用的参数,并花费更多时间来决定该做什么。

MsgBoxExtendedFunctionality

扩展功能类(MsgBoxExtendedFunctionality)为标准消息框的所有可用/未来扩展提供了一站式服务。它只是所有扩展功能对象的容器,使扩展功能的使用变得更加简单。其思想是用户将根据给定情况所需的内容填充此对象。事实上,只需要包含给定消息框实例所需的那些对象。如果未填充的对象可能影响其当前属性值,消息框将简单地使用其当前属性值。

字符串详细信息文本 获取/设置将显示在详细信息扩展器中的文本。如果此属性被填充(非空/非空),则详细信息扩展器将自动显示。此属性与任何其他扩展功能无关。
MsgBoxExCheckBoxData CheckBoxData 获取/设置 CheckBoxData 对象。此属性与任何其他扩展功能无关。
MsgBoxExDelegate MessageDelegate 获取/设置消息委托对象。此对象允许消息框图标可点击,并提供一种执行代码以响应点击的机制。
bool ExitAfterAction 获取/设置标志,指示在运行消息委托操作后应关闭消息框。
string DelegateToolTip 获取/设置当用户将鼠标悬停在消息框图标上时显示的工具提示
MsgBoxUrl URL/b> 获取/设置消息框 URL 对象,该对象会导致在消息框中显示可点击的 URL。
MessageBoxButtonDefault ButtonDefault 获取/设置消息框上使用的默认按钮。如果未指定按钮,则使用 Windows.Forms.MessageBox 中通常使用的按钮。

使用此类别相当简单,您可以使用对象初始化器填充它,如下所示

MsgBoxExtendedFunctionality ext = new MsgBoxExtendedFunctionality()
{
// Default button - you only need to set this if you want to use something 
// other than the standard Windows.Forms.MessageBox default button 
staticButtonDefault = this.ButtonDefault;

// clickable icon
MessageDelegate  = this.errorDelegate,
ExitAfterAction  = true,
// you can also set the tooltip text, but for the purposes of this demo, we're 
// using the default value
//,DelegateToolTip = null

// checkbox
CheckBoxData     = this.checkboxData,

// details
DetailsText      = this.Testmsg(),

// url
    
URL              = new MsgBoxUrl()
{
    URL          = new Uri("http://www.google.com"),
    DisplayName  = "Google",
    Foreground   = Colors.LightBlue
}
};

当您查看构造函数时,您会发现所有内容都设置为某个默认值,以确保每次调用 MessageBoxEx.ShowEx() 都使用自己的设置。

public MsgBoxExtendedFunctionality()
{
this.ButtonDefault   = MessageBoxButtonDefault.Forms;
this.DetailsText     = null;
this.CheckBoxData    = null;
this.MessageDelegate = null;
this.URL             = null;
this.DelegateToolTip = "Click this icon for additional info/actions.";
}

如果您想添加新的扩展功能,请创建您的扩展功能对象,并将其作为属性添加到此类别中。您可能还需要在 MessageBoxEx 中添加更多的静态配置方法来处理新功能的设置。

MsgBoxExCheckBoxData

此对象允许您创建一个可绑定的对象,该对象可以在消息框外部(在显示消息框的代码中)进行评估,并且最有可能用于允许应用程序用户选择不查看给定消息框。

public class MsgBoxExCheckBoxData : INotifyPropertyChanged
{
#region INotifyPropertyChanged

private bool isModified = false;
public bool IsModified { get { return this.isModified; } set { if (value != this.isModified) { this.isModified = true; this.NotifyPropertyChanged(); } } }
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;

/// <summary>
/// Notifies that the property changed, and sets IsModified to true.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        if (propertyName != "IsModified")
        {
            this.IsModified = true;
        }
    }
}
    
#endregion INotifyPropertyChanged

private string checkBoxText;
private bool   checkBoxIsChecked;

/// <summary>
/// Get/set the text content of the checkbox
/// </summary>
public string CheckBoxText      
{ 
    get { return this.checkBoxText; } 
    set 
    { 
        if (value != this.checkBoxText) 
        { 
            this.checkBoxText = value; 
            this.NotifyPropertyChanged(); 
        } 
    } 
}
/// <summary>
/// Get/set the flag that indicates whether the checkbox is checked
/// </summary>
public bool CheckBoxIsChecked 
{ 
    get { return this.checkBoxIsChecked; } 
    set 
    { 
        if (value != this.checkBoxIsChecked) 
        { 
            this.checkBoxIsChecked = value; 
            this.NotifyPropertyChanged(); 
        } 
    } 
}
}

一种可能的用法是这样的

MsgBoxExCheckBoxData checkboxData = new MsgBoxExCheckBoxData()
{ 
CheckBoxIsChecked = false, 
CheckBoxText = "Don't show this message any more"
};
MsgBoxExtendedFunctionality ext = new MsgBoxExtendedFunctionality()
{
CheckBoxData = this.checkboxData
};
MessageBoxEx.ShowEx("Message text", ext);

当消息框返回时,ext.CheckBoxData 对象包含在消息框中设置的复选框的当前选中状态。如何持久化此值完全取决于您。

MsgBoxExDelegate

 

public abstract class MsgBoxExDelegate
{
/// <summary>
/// Get/set the message text from the calling message box
/// </summary>
public string   Message     { get; set; }
/// <summary>
/// Get/set the details text (if it was specified in the messagebox)
/// </summary>
public string   Details     { get; set; }
/// <summary>
/// Get/set the message datetime at which this object was created
/// </summary>
public DateTime MessageDate { get; set; }

/// <summary>
/// Performs the desired action, and returns the result. MUST BE OVERIDDEN IN INHERITING CLASS. 
/// </summary>
/// <returns></returns>
public virtual MessageBoxResult PerformAction(string message, string details = null)
{
    throw new NotImplementedException();
}
}

此类允许您使消息框图标可点击,并且当它被点击时,执行此对象中包含的代码(PerformAction 方法)。此(抽象)类必须被继承,并且当图标被点击时,PerformAction 方法将被执行。

MsgBoxUrl

public class MsgBoxUrl
{
/// <summary>
/// Get/set the web link. Any Uri type other than "http" is ignored. The URL is also used for the tooltip.
/// </summary>
public Uri                        URL         { get; set; }
/// <summary>
/// Get/set the optional display name for the web link
/// </summary>
public string                     DisplayName { get; set; }
/// <summary>
/// Get/set the foreground color for the web link
/// </summary>
public System.Windows.Media.Color Foreground  { get; set; }

public MsgBoxUrl()
{
    // default color
    this.Foreground = MessageBoxEx.DefaultUrlForegroundColor;
}

此类允许您在消息框中指定用户可以点击的 URL。

公共静态配置方法

本节中描述的所有 ShowEx() 方法旨在提供超出标准消息框的扩展功能。可用的重载与标准 Show() 方法相同。这些方法要求您将扩展功能对象指定为第一个参数。

SetMessageBackground(System.Windows.Media.Color color) 设置消息背景颜色。
SetMessageForeground(System.Windows.Media.Color color) 设置消息前景色。
SetButtonBackground(System.Windows.Media.Color color) 设置按钮面板背景颜色。
SetFont() 根据应用程序主窗口设置字体系列/大小
SetFont(ContentControl parent) 根据指定的 ContentControl 设置字体系列/大小
SetFont(string familyName, double size) 设置字体系列/大小。
SetButtonTemplateName(string name) 指定所需的按钮模板名称。
SetMaxFormWidth(double value) 设置最大窗口宽度。
ResetToDefaults() 将所有配置项重置为默认值。
SetAsSilent(bool quiet) 切换系统声音(与图标关联)开/关。
SetDefaultButton( 设置按钮默认值。

还有其他公共配置和 Show...() 方法,但它们都已弃用,不应使用。

字符串详细信息文本 获取/设置表示消息详细信息的文本字符串。
MsgBoxExCheckBoxData CheckBoxData 获取/设置启用复选框显示的对象。
MsgBoxExDelegate MessageDelegate 获取/设置使消息框图标可点击并提供点击图标时要执行的操作的对象。
bool ExitAfterAction 获取/设置标志,指示在执行消息框委托操作后应关闭消息框。
string DelegateToolTip 获取/设置可点击图标工具提示文本。
MsgBoxUrl URL 获取/设置允许显示可点击 URL 的对象。

历史

  • 2021.01.15 - 更改列表
    修复的 Bug
    • 修复了 LargestButtonWidth “不工作”的问题。实际问题是我在设置字体族/大小之前调用了此方法。
       
    • 修复了当您尝试从窗口的构造函数显示消息框时导致异常的问题。
       
    • 修复了如果您指定不存在的自定义按钮模板名称时不会显示按钮的问题。 
    新增功能
    • 添加了 ResetToDefault() 方法,将所有 MessageBoxEx 属性重置为其默认值。
       
    • 添加了一个用于 Powershell 的可执行文件。请参阅本文 -
       
    • 当我在处理上一个项目时,我发现 WPF 消息框与 Windows.Forms 版本不同。我添加了 WPF 版本消息框中缺少(而 Windows.Forms 版本有)的按钮——AbortRetryIgnoreRetryCancel。这适用于 Powershell 消息框应用程序和常规 MessageBoxEx 代码。
       
    • 增加了指定默认按钮的功能。 
    维护事项
    • MessageBoxEx 类移到 DLL 中。这允许您将其包含在应用程序中,而无需添加应用程序通常不会包含的引用,同时还允许我将各种类分离到各自的文件中。
       
    • 删除了已弃用的 ShowWithCheckBoxShowWithDetailsShowWithCheckBoxAndDetails 方法,如果您仍在使用它们,请切换到 ShowEx 方法。
       
    • 我将所有旧的 Show()ShowEx() 重载都用编译器定义括起来(详见文章内容)。
    • 添加了用于验证指定字体系列名称的代码(如果您使用 SetFont(familyName, size) 重载)。
       
    • 为可点击的 URL 添加了默认颜色(蓝色)。
       
    • 删除了所有文件中未使用的 using 语句。
       
    • 修改了 maxFormWidth 的处理方式。默认值现在是主屏幕工作区宽度减去 100 像素。
       
    • 在文章中添加了类参考部分,以便您可以看到哪些方法可用。 
    • 将项目移至 VS2019,因为 VS2107 不允许在智能感知注释中使用换行符,而我在注释中有很多重要内容要说。由于代码使用的是 .Net Framework 4.72,您仍然可以在 VS2017 中加载解决方案而没有问题。 
    文章内容
    • 在文章中添加了类参考部分,以便您可以看到哪些方法可用。 
    • 修正了许多拼写错误。 

     
  • 2021.01.08 - 增加了显示可点击 URL 的功能,取消了对消息委托功能任意的仅错误限制,并重构了状态 Show 方法的变体,以简化维护并更容易采用新功能。
     
  • 2021.01.07 - 编辑了文章,加入了更多关于复选框、详细信息和可点击错误图标的信息。下载的 ZIP 文件没有更改。
     
  • 2021.01.06 - 添加了详细信息、复选框和可点击的错误图标,并从文章正文中删除了代码转储。当然,现在有一个新的可下载的源代码 ZIP 文件。最后,我修复了用户使用标题栏中的关闭按钮关闭消息框时的返回值。
     
  • 2021.01.04 - 使 MessageBoxIcon 对齐方式与标准 MessageBox 相同。我还尝试修复了导致消息框被替换为应用程序最顶层窗口的问题。最后,我更新了屏幕截图,并包含了标准 MessageBox 的屏幕截图,以便在文章中进行更直观的视觉比较。再次强调,我没有更新文章中显示的代码,但有一个新的 ZIP 文件可供下载。
     
  • 2021.01.03 - 增加了对 MessageBoxIcon 和系统声音的支持,将消息显示改为使用 TextBox,以便您可以将消息复制到剪贴板,并更改了一些控件对齐方式以使其“更好”。我没有更新文章中显示的代码。
     
  • 2020.12.25 - 初次发布
     
© . All rights reserved.