通用的MessageBox替换
一个包含一些急需功能的MessageBox替换。
引言
我是一个非常重视本地化的“程序员”。由于我们在世界各地工作,这并不总是那么容易。幸运的是,我们的大多数客户都受过高等教育,英语说得很好。尽管如此,我还是关心本地化我们的应用程序。
其中一件让我一直感到恼火的事情是,至少在 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 个。
在应用程序中,您可以选择使用哪个 messagebox
:系统版本还是我的版本。当然,有些选项仅在使用扩展版本时可用:要使用其他图标,在使用扩展框时可以选择另外两个图标。当然,这只是一个例子,在实际生活中,您可以提供自己的 32 * 32 像素图标。要测试各种字体和颜色,请单击字体按钮或颜色标签。对话框将帮助您选择您喜欢的。超时时间(以秒为单位)通过超时数字输入给出。另外两个复选框帮助您指定是否显示*复选框*或表单是否以所有者为中心。然后单击“**尝试**”。
选择一个选项后,您可以看到正确的值显示在按钮旁边。如果存在复选框,也会显示该*复选框*的状态。
我希望这个 messagebox
替换版本能帮助您创建更出色的程序。欢迎提出任何建议、批评和补充(更多语言!)。
而且不要忘记:祝您编码愉快!
历史
- 2012 年 9 月 6 日 - 创建了文章
- 2012 年 10 月 8 日 - 修复了多位读者报告的若干个错误