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

通用的MessageBox替换

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (22投票s)

2012 年 9 月 6 日

CPOL

7分钟阅读

viewsIcon

58354

downloadIcon

2258

一个包含一些急需功能的MessageBox替换。

MessageBoxEx form

引言

我是一个非常重视本地化的“程序员”。由于我们在世界各地工作,这并不总是那么容易。幸运的是,我们的大多数客户都受过高等教育,英语说得很好。尽管如此,我还是关心本地化我们的应用程序。

其中一件让我一直感到恼火的事情是,至少在 Windows™ XP 下,MessageBox 根本不理会 Thread.CurrentThread.CurrentUICulture。无论我将其设置为任何值,messagebox 按钮都会使用已安装 Windows 版本的语言。所以,我利用一些空闲时间,决定寻找一个解决方案。我“谷歌”又“谷歌”,但找到的所有文章都没有令人满意地解决这个问题。所以经过一番考虑,我决定编写一个 MessageBox 替换版本。

为了感谢 CodeProject 上大家提供的许多提示和技巧,我也决定写这篇文章。

要求

这个替换版本应该满足一些要求

  • 它应该提供对 **所有** MessageBox 方法的透明替换。也就是说:如果我使用*搜索和替换*将 MessageBox 更改为 MessageBoxEx(这是我决定的名称)并添加了正确的引用,那么就不会出现问题,因为有人调用了我代码不支持的 messagebox 方法。
  • 在*正常*配置下,它应该尽可能接近真实版本。
  • 它应该能够检测 Thread.CurrentThread.CurrentUICulture 并相应地更改按钮标题。

而且,既然我决定编写自己的 messagebox,我希望包含一些 Microsoft 版本中缺失的功能。它们是:

  • 允许将消息居中在调用它的表单上,而不是屏幕中央。
  • 应该可以设置一个超时时间,之后将采取默认操作。由于我们的一些程序计算量非常大,我们倾向于在离开办公室时启动它们。第二天早上回来时,发现程序一直在等待用户确认而没有完成,这非常令人沮丧。
  • 为什么只有 Microsoft 版本中的四个图标?所以我想指定我自己的,更合适的图标。
  • 有时,人们希望给用户一个选项,让他们不必反复报告同一个问题。因此,一个可以勾选“不再显示此消息”的*复选框*会很不错。
  • 而且既然我在忙,为什么不包括更改字体或颜色的可能性?还有不透明度?

未来的添加可能包括定义自己的标题的可能性。

挑战

于是我开始了项目。其中大部分相对直接。但很快就出现了一些有趣的问题,或者说挑战。

按钮的尺寸

我不知道 Microsoft 是怎么做的,但当你支持多种语言(和多种字体)时,你不能简单地给表单和按钮一个固定的尺寸。另一方面,我也不希望按钮的大小随着显示的按钮而变化。一旦选择了语言,所有按钮都应该具有相同的大小。幸运的是,文本的数量相当少,所以解决方案是测量所有可能的标题,然后选择最长的标题来确定按钮的大小。为了获得美观的外观,我发现*相对于*测量尺寸的大小效果最好。我本可以选择应用固定边距,但这看起来更好。

执行此操作的代码

private static System.Drawing.SizeF measureButtons(Font usedFont)
{
    SizeF maxSize = new Size(1,1);
    SizeF size;

    // Measure all button captions in current culture and find widest:

    size = measureString(Deltares.Controls.Properties.Resources.buttonTextAbort, usedFont);
            if (size.Width > maxSize.Width) maxSize = size;
.....

    // Apply padding:

    maxSize = new SizeF((int)(1.6f * maxSize.Width), (int)(1.75f * maxSize.Height));
    return maxSize;
}

显示帮助

下一个问题是显示帮助。有许多 Show 方法会导致 messagebox 添加一个帮助按钮。添加按钮不是问题,但 messagebox 在不知道显示哪个文件的情况下如何工作?经过一些实验,我发现原始框可能使用了主表单上 HelpProvider 的信息。如果我没有提供,按下帮助按钮将不起作用。如果我有一个并提供了正确的 HelpNamespace,按下帮助按钮就会显示文件。好的,现在我需要模仿这种行为。这让我深入研究了 System.Reflection 命名空间。但我确实解决了!只需遍历 EntryAssembly(可能包含 HelpProvider 的那个)中的所有类型。然后检查该类型是否为表单。如果是,则遍历其字段以查找其中是否有 HelpProvider。如果是,则使用 Activator 创建一个实例,并对字段使用 GetValue 以访问此 HelpProvider。检查是否提供了命名空间,如果提供了,我们就完成了。

    Assembly ass = Assembly.GetEntryAssembly();
    Type[] types = ass.GetTypes();
    foreach (Type type in types)
    {
        if (type.BaseType.Equals( typeof(System.Windows.Forms.Form)))
        {
            FieldInfo[] fields = type.GetFields(BindingFlags.Instance | 
                                 BindingFlags.Public | BindingFlags.NonPublic);
            foreach (FieldInfo fi in fields)
            {
                if (fi.FieldType.Equals(typeof(System.Windows.Forms.HelpProvider)))
                {
                    // Yes, we have a form with a HelpProvider. 
                    // Use reflection to create an instance if it:
                    object  inst = Activator.CreateInstance(type);
                    System.Windows.Forms.HelpProvider hp = 
                            fi.GetValue(inst) as System.Windows.Forms.HelpProvider;
                    // If we have succeeded and the provider has a non-null HelpNamespace, 
                    // take the value as the helpfile:
                    if ((hp != null) && (hp.HelpNamespace != null))
                    {
                        return hp.HelpNamespace;
                    }
                }
            }
        }
    }

您会在 MessageBoxEx 类的 getHelpFilePath 方法中找到上述代码。

获取额外信息(进出)

为了将所有额外信息传递给需要它的 messagebox(字体、颜色等),我可以选择创建大量的重载。添加到已有的 21 个之外,这将产生过多的变体。所以我决定只添加 21 个新的重载方法,每个方法都在参数列表中添加一个额外的结构。然后,该结构将包含额外功能所需的所有信息。因此,MessageBoxExtras

使用超时

要设置超时时间,您可以输入等待的毫秒数。它必须是 1 到 86400000(一天)之间的值。为了告知用户该框将消失,我显示了一个倒计时进度条。一旦它达到*0*,就会执行由 defaultButton 参数定义的默认操作。然而,系统进度条太丑了……所以我添加了一个简单的“子类化”进度条。整个进度条通过 MessageBoxExtras 提供,您可以使用系统栏和替换栏。您可以通过设置正确的高度来决定栏的突出程度,然后再传递它。

为了从指定时间倒计时,我使用了一个计时器。但如果您使用 System.Windows.Forms.Timer,正在运行屏幕更新的线程将被频繁阻塞,导致进度条更新不够频繁。所以我使用了一个 System.Threading.Timer。但因为该计时器可以在线程池的任何线程上运行,所以在更新进度条时必须采取额外的预防措施。因此,每次滴答执行的代码都会检查是否需要 Invoke,如果需要,则使用回调到该方法。

还有一些其他困难,但您可以查看代码来找出我是如何解决它们的。

示例应用程序

在开始为 MessageBoxEx 类编写代码之前,我创建了一个示例应用程序来测试原始版本和我的新版本。我只需要知道原始版本是如何工作的,才能创建自己的版本。我可以(也应该?)创建一个测试所有 42 个重载的 Show 方法的应用程序,但我决定只测试其中的 4 个。

Sample application

在应用程序中,您可以选择使用哪个 messagebox:系统版本还是我的版本。当然,有些选项仅在使用扩展版本时可用:要使用其他图标,在使用扩展框时可以选择另外两个图标。当然,这只是一个例子,在实际生活中,您可以提供自己的 32 * 32 像素图标。要测试各种字体和颜色,请单击字体按钮或颜色标签。对话框将帮助您选择您喜欢的。超时时间(以秒为单位)通过超时数字输入给出。另外两个复选框帮助您指定是否显示*复选框*或表单是否以所有者为中心。然后单击“**尝试**”。

选择一个选项后,您可以看到正确的值显示在按钮旁边。如果存在复选框,也会显示该*复选框*的状态。

我希望这个 messagebox 替换版本能帮助您创建更出色的程序。欢迎提出任何建议、批评和补充(更多语言!)。

而且不要忘记:祝您编码愉快!

历史

  • 2012 年 9 月 6 日 - 创建了文章
  • 2012 年 10 月 8 日 - 修复了多位读者报告的若干个错误
© . All rights reserved.