自定义消息框的设计与实现

在全球化的软件开发中,本地化是一个不可忽视的重要环节。尽管客户大多受过良好教育,英语水平很高,但仍然非常关注应用程序的本地化。在Windows XP操作系统下,一个让感到困扰的问题是,无论怎么设置Thread.CurrentThread.CurrentUICulture,MessageBox的按钮语言始终是Windows安装版本的语言。因此,决定寻找解决方案。经过一番搜索,发现现有的文章并没有让满意,于是决定自己编写一个消息框的替代品。

需求分析

这个替代品需要满足以下要求:

  • 能够透明地替换所有MessageBox方法。
  • 在正常配置下,外观应尽可能接近原生消息框
  • 能够检测Thread.CurrentThread.CurrentUICulture,并相应地更改按钮标题。
  • 提供将消息居中显示在调用表单上的功能,而不是屏幕中心。
  • 可以指定一个超时时间,之后将采取默认操作。
  • 允许自定义图标,而不仅仅是微软版本中的四个图标。
  • 提供一个复选框,让用户可以选择不再显示相同的消息。
  • 允许更改字体或颜色以及透明度。

挑战与解决方案

在项目开始时,许多问题都是相对简单的。但是,随着项目的深入,一些有趣的问题或挑战逐渐显现出来。

在支持多种语言(和多种字体)的情况下,不能简单地给表单和按钮一个固定的大小。另一方面,也不希望按钮的大小随着显示的按钮而变化。一旦选择了一种语言,所有按钮的大小应该保持一致。幸运的是,文本数量相当有限,因此解决方案是测量所有可能的标题,并选择最宽的来确定按钮大小。为了美观,发现相对于测量大小的大小效果最好。

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方法会导致消息框添加一个帮助按钮。添加按钮没有问题,但是消息框是如何在不知道要显示哪个文件的情况下工作的呢?经过一些实验,发现原始框可能使用了主表单上的HelpProvider的信息。如果没有提供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. 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; } } } } }

为了将所有额外的信息传递给需要它的messagebox(字体、颜色等),可以选择创建许多新的重载。加上已经存在的21个,那将创建太多变体。所以决定只添加21个新的重载方法,每个方法在参数列表中添加一个额外的结构。该结构将包含所有额外功能所需的信息。因此有了MessageBoxExtras。

要指定超时,可以输入要等待的毫秒数。它必须在1到86400000(一天)之间。为了通知用户该框将消失,显示了一个倒计时进度条。一旦它达到0,将采取defaultButton参数定义的默认操作。但是系统的进度条太丑了...所以添加了一个简单的'子类化'进度条。整个进度条通过MessageBoxExtras提供,可以使用系统条和替代条。可以通过设置正确的高度来决定条的突出程度。

示例应用程序

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

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

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

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485