WPF TaskDialog 包装器和模拟器






4.92/5 (41投票s)
一个 TaskDialog 包装器类,带有一个回退模拟器(适用于 XP 及更早版本)。
简而言之
快速来说,您需要知道的是:
- 适用于 WPF 应用程序的 .NET 4.0 版 Visual Studio 2010 解决方案
- 在可用时使用本机
TaskDialogIndirect
调用(Vista/7) - 在不可用时使用 WPF 的模拟版本(XP)
- 提供无缝回退、大量可自定义选项和详细的返回结果
- 完全免费使用或修改
- v1.5+ 中有大量改进——请参见下文!
引言
我一直是 Hedley Muscroft 的 TaskDialogs 的 WinForms 模拟器和包装器的忠实粉丝(和用户)。本着同样的精神,我借鉴了他的部分工作(以及 KevinGre 的工作),做了非常相似的事情,但这次是针对 WPF(和 .NET 4.0)。
背景
市面上有很多其他的 WPF/WinForms TaskDialog 实现,要么是包装 Win32 API,要么只是提供一个自定义的 WPF 对话框供使用。快速的网络搜索就能找到很多。它们没有提供一种统一的方式来使用仍然能在旧版 Windows 上运行的原生对话框。我在这篇博客文章中更详细地阐述了所有这些的原理和历史。
Hedley 的作品很棒,我用过很多次,但我想要一个非常相似、适用于 WPF 应用程序的东西。我也想花点时间来改进它,这主要得益于 WPF 和 .NET 4.0 的强大功能。
Using the Code
在大多数情况下,您只会关心 TaskDialog
类的 `static` 方法来调用对话框。我希望它能模仿传统 MessageBox
类一直以来提供的易用性。您将使用 `static` 方法的另外两个主要类是:
TaskDialogOptions
TaskDialogResult
在需要完全控制任务对话框自定义的情况下,您需要创建一个选项对象并将其传递给 `Show` 方法。这意味着您不能再进行单行方法调用,但这确实为您提供了最重要的控制权。
在简单的情况下,您将收到一个 `enum` 值,类似于 MessageBox
的返回值(事实上,它可以直接转换为 DialogResult
),该值将告诉您用户单击了哪个按钮。在更复杂的情况下,尤其是在使用选项对象时,您将收到一个完整的返回对象,其中包含您需要了解的有关用户如何与任务对话框交互的所有信息。您将获得选中的任何单选按钮、命令链接或通用/自定义按钮的索引。
下面是一些显示对话框的代码
TaskDialogOptions config = new TaskDialogOptions();
config.Owner = this;
config.Title = "Task Dialog Title";
config.MainInstruction = "The main instruction text for the TaskDialog goes here";
config.Content = "The content text for the task dialog is shown " +
"here and the text will automatically wrap as needed.";
config.ExpandedInfo = "Any expanded content text for the " +
"task dialog is shown here and the text " +
"will automatically wrap as needed.";
config.VerificationText = "Don't show me this message again";
config.CustomButtons = new string[] { "&Save", "Do&n't save", "&Cancel" };
config.MainIcon = VistaTaskDialogIcon.Shield;
config.FooterText = "Optional footer text with an icon can be included.";
config.FooterIcon = VistaTaskDialogIcon.Warning;
TaskDialogResult res = TaskDialog.Show(config);
以下是在 Windows 7 和 Windows XP 上模拟的相应对话框
可以指定快捷键(如 TaskDialogs 的 Win32 API),方法是在字符前加上一个 ampersand (&),如上面的代码所示。您可能已经知道,在 WPF 中,快捷键用下划线表示,但模拟对话框会自动处理这种差异。
可以通过在文本的某个部分使用 HTML 等效的 anchor 标签来指定可点击的超链接。只有 `Content`、`ExpandedInfo` 和 `FooterText` 属性支持超链接。您可以使用回调处理程序来捕获点击。在回调中可以使用 `href` 属性中指定的任何值。测试项目中有一个示例。
使用选项对象时,您留空或未指定的任何内容都将不会显示,这使其非常灵活。您还可以修改 `static` 的 `TaskDialogOptions.Default` 实例来设置默认值,并使用它来创建未来的新选项对象。例如,将标题始终显示您的应用程序名称可能很有用,除非另有指定。由于选项对象是 `struct` 值类型,您将编写 `TaskDialogOptions config = TaskDialogOptions.Default;` 而不是使用上面所示的 `new` 运算符。
源代码中包含一个测试应用程序,与 Hedley 的类似,它允许您尝试各种不同的类型,并查看原生(当然,前提是您在 Vista/7 上)和模拟版本,以及它们返回的信息类型。我鼓励您查看测试项目的代码,了解它是如何显示任务对话框的,从而了解如何创建各种类型的对话框。
更多示例
以下示例分别显示了调用代码以及原生、在 Windows 7 上模拟以及在 Windows XP 上模拟的相应对话框。您可以轻松地看到,即使在 XP 上,模拟对话框也非常接近原生对话框,并且图标也特定于运行的操作系统版本。
显示完整的错误消息
TaskDialog.ShowMessage(this,
"Outlook",
"ActiveSync can't log on to Outlook",
"Make sure that Outlook is installed and functioning correctly.",
"You may need to restart your computer. You could have a conflict "
+ "due to two folders on this computer are name C:\\Program Files\\Microsoft "
+ "and C:\\Program Files\\Microsoft Office. If so, rename the "
+ "C:\\Program Files\\Microsoft folder so that it does not contain the word "
+ "\"Microsoft.\" If this folder contains subfolders, you may need "
+ "to reinstall programs in the renamed folder.\n\nFor more information "
+ "on renaming folders and installing programs, see Help for your "
+ "operating system.",
null,
null,
TaskDialogCommonButtons.Close,
VistaTaskDialogIcon.Error,
VistaTaskDialogIcon.None);
显示单选按钮选项
TaskDialogOptions config = new TaskDialogOptions();
config.Owner = this;
config.Title = "RadioBox Title";
config.MainInstruction = "The main instruction text for the TaskDialog goes here.";
config.Content = "The content text for the task dialog is shown here "
+ "and the text will automatically wrap as needed.";
config.ExpandedInfo = "Any expanded content text for the task dialog "
+ "is shown here and the text will automatically wrap as needed.";
config.RadioButtons = new string[] {
"Radio Option 1", "Radio Option 2",
"Radio Option 3", "Radio Option 4", "Radio Option 5" };
config.MainIcon = VistaTaskDialogIcon.Information;
TaskDialogResult res = TaskDialog.Show(config);
显示命令链接
TaskDialogOptions config = new TaskDialogOptions();
config.Owner = this;
config.Title = "RadioBox Title";
config.MainInstruction = "The main instruction text for the TaskDialog goes here.";
config.Content = "The content text for the task dialog is shown here "
+ "and the text will automatically wrap as needed.";
config.ExpandedInfo = "Any expanded content text for the task dialog "
+ "is shown here and the text will automatically wrap as needed.";
config.CommandButtons = new string[] {
"Command &Link 1", "Command Link 2\nLine 2\nLine 3", "Command Link 3" };
config.MainIcon = VistaTaskDialogIcon.Information;
TaskDialogResult res = TaskDialog.Show(config);
使用自定义图标
将图标添加到您的项目中,并将生成操作设置为 Resource。我只是通过默认的 _Resources.resx_ 文件将其添加为 Image 资源。
TaskDialogOptions config = new TaskDialogOptions();
config.Owner = this;
config.Title = "Windows Genuine Verification";
config.MainInstruction = "This copy of Windows is not genuine.";
config.Content = "You may be a victim of software counterfeiting.";
config.CommonButtons = TaskDialogCommonButtons.Close;
config.CustomMainIcon = System.Drawing.Icon.FromHandle
(Properties.Resources.genuine_32.GetHicon());
TaskDialogResult res = TaskDialog.Show(config);
别忘了完成后将其处置!(例如:`config.CustomMainIcon.Dispose();`)
显示进度条
TaskDialogOptions config = new TaskDialogOptions();
config.Owner = this;
config.Title = "Downloading File...";
config.MainInstruction =
"Your file 'en_visual_studio_2010_ultimate_x86_dvd_509116.iso' is currently downloading";
config.Content = "Time elapsed: 00:00 | Download rate: 0 KB/s";
config.CustomButtons = new string[] { "&Reset Timer", "&Cancel" };
config.AllowDialogCancellation = true;
config.ShowProgressBar = true;
config.EnableCallbackTimer = true;
config.Callback = taskDialog_Callback2;
TaskDialogResult res = TaskDialog.Show(config);
在回调处理程序中,您将处理计时器事件来更新进度条的值,或者可以选择使用跑马灯(不确定)样式。我在建议您查看的测试项目中有一个相当不错的示例实现。回调是一些比较棘手的事情,但它们可以非常强大。
已知限制/错误
目前,尽管这些功能在原生 `TaskDialog` 中有效,但此包装器(尚未)不支持它们
- 窗口位置
- 从右到左的布局
- 自动和自定义窗口大小
- 在 Windows XP 上,模拟对话框有时仍然会显示图标(原生 `TaskDialog` 和 Vista/7 上的模拟对话框从不显示图标)
此外,模拟表单在切换显示/隐藏详细信息按钮时,可能需要更智能的自动调整大小逻辑。原生对话框似乎具有此功能,但其确切规则并不明确或显而易见。我也没 bother 去尝试实现原生对话框在切换显示/隐藏详细信息按钮时使用的花哨的淡入淡出和调整大小动画。从屏幕截图上看并不明显,但模拟的命令链接非常接近原生,并且也具有所有动画。
请在下面的评论中留下任何错误、更正、建议或问题。 我通常会尽快回复。
历史
- 2010 年 12 月 17 日 - **v1.0**
- 首次公开发布
- 2011 年 2 月 7 日 - **v1.0.1**
- 现在使用 Visual Studio 的 DesignData 功能
- 修复了默认单选按钮选择的错误(感谢 pt1401)
- 模拟 Task Dialog 现在已正确忽略 Alt-F4(如果适用)
- 2011 年 11 月 1 日 - **v1.1**
- 为 `TaskDialogConfig` 添加了 `AllowDialogCancellation` 属性
- 2011 年 11 月 2 日 - **v1.1.1**
- 修复了 `DefaultButtonIndex` 以便与原生 Task Dialog 一起工作(感谢 Filip D'haene)
- 2011 年 12 月 21 日 - **v1.5**
- 添加了对回调、进度条、超链接以及自定义主/页脚图标的支持
- 添加了对更多自定义选项的支持:展开页脚(默认是内容)、默认展开、验证复选框的默认状态
- 虽然核心任务对话框代码现在大部分已经稳定,但这些新功能目前应被视为实验性的
- 修复了 `RadioButtonResult` 以便不覆盖 `SimpleResult`,因此您仍然可以看到用户单击了哪个按钮(例如 Cancel),以及选择了哪个单选项目(部分归功于 awrdsemb)
- 2012 年 9 月 20 日 - **v1.6**
- 添加了对回调期间 ClickButton 的支持(请参阅源代码中的演示项目以获取示例用法)
- 回调计时器现在在模拟对话框关闭时正确停止(感谢 niemyjski)
- 更改了模拟对话框,在未指定按钮时显示 OK 按钮而不是 Close
- 2012 年 10 月 18 日 - **v1.7**
- 添加了按类型和索引的其他按钮单击方法,用于回调
- 回调参数现在包括按钮索引,如果通知与单击相关
- 单选按钮现在也可以通过编程方式单击
- 在回调中添加了对按钮启用/禁用以及设置所需权限的支持
- 有关 `TaskDialogCallback` XML 文档中返回值的更多信息
- 模拟表单现在在显示单选按钮时正确支持通用按钮和自定义按钮,以及在未指定其他按钮时提供默认的 OK 按钮
- 模拟表单现在在对话框通过 Esc、Alt+F4 等关闭时正确通知回调
- 模拟表单不再错误地交换 Retry/Cancel 按钮