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

TDXML:带可视化任务对话框编辑器,基于 XML 的任务对话框

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (64投票s)

2007 年 3 月 19 日

13分钟阅读

viewsIcon

104630

downloadIcon

1410

一个库和一个可视化编辑器,可以轻松地在 C++ 应用程序中构建和使用任务对话框。

目录

引言

如果您阅读过我关于 Vista 任务对话框的前两篇文章(第一部分第二部分),您就知道任务对话框是一项很棒的新 UI 功能,但使用起来可能很麻烦。在 2005 年 PDC 上,会议 PRS319(“构建在 Windows Vista 中外观出色的应用程序”)的演讲者演示了一个由 shell 团队开发的名为 TDPad 的工具。TDPad 是一个简单的任务对话框编辑器,它在内部使用 XML 数据文件,并执行一些繁重的工作,但仍需要大量手动工作才能将 TDPad 的输出集成到您的应用程序中。

本文介绍了 TDXML(基于 XML 的任务对话框)库,与直接调用 TaskDialog()TaskDialogIndirect() API 相比,它的设计目标是更易于使用。它还包含一个可视化任务对话框编辑器,您可以使用它来生成 TDXML 使用的 XML 输入。当然,您也可以自己创建 XML,但我认为可视化编辑器提供的即时反馈对开发人员来说更友好。

TDXML 库的设计目标是:

  • 完全数据驱动:您无需更改任何 C++ 代码即可更改任务对话框的外观。
  • 使用广泛可用的 XML 解析器:TDXML 使用 MSXML 解析器,该解析器存在于所有现代系统中。
  • 可本地化:XML 可以作为文件与您的应用程序一起存储,或放入资源中。无论哪种方式,都可以轻松本地化。该库的数据驱动特性意味着您可以在不修改或重新编译任何 C++ 代码的情况下本地化 XML。
  • 调用者方便:使用任务对话框,您无需执行任何比初始化 C++ 对象并调用 DoModal() 更复杂的操作,这与在 MFC 中使用 CDialog 和在 ATL 中使用 CDialogImpl 类似。

本文使用 Visual Studio 2005、WTL 7.5Windows SDK 针对 Vista 的 RTM 版本编写。有关在哪里可以下载这些组件的更多信息,请参阅第一个 Vista Goodies 文章中的介绍。TDXML 库使用 CString_bstr_t 字符串包装类;我只在 WTL 应用程序的 VC 2005 上测试过该代码,但由于这些字符串类也适用于 MFC,因此 TDXML 库应该可以轻松地集成到 MFC 应用程序中。

使用可视化编辑器

概述

任务对话框编辑器包含在演示下载的TDEditor项目中,您可以使用它来设置任务对话框,确切地了解它在您的程序中的外观,然后生成一个 XML 文件供 TDXML 使用。运行 TDEditor 时,您会看到四个页面,您可以在其中设置任务对话框的各种 UI 元素。这是它最初的外观(编辑器的屏幕截图以缩略图形式显示,以保持在 CodeProject 的文章宽度限制内)。

TDEditor 还显示了任务对话框的实时预览,使用了您在编辑器中输入的当前文本和设置。这是一个真正的任务对话框,而不是模拟,因此您可以确信您看到的就是您将要得到的。这是运行 TDEditor 时任务对话框的初始状态。

在大多数情况下,TDEditor 会自动更新任务对话框以反映您在编辑器中所做的任何更改,但有少数设置无法实现。当您更改此类设置时,会在Rebuild Dialog按钮上添加一个信息图标,告知您需要重新构建对话框才能看到更改。如果您在任务对话框本身中进行了更改,例如单击展开/折叠按钮或选择单选按钮,您可以通过单击Rebuild Dialog来丢弃这些更改。单击下面的缩略图查看显示实时编辑效果的示例动画。

一旦您对任务对话框的设置满意,就可以生成一个包含您输入到编辑器中的所有信息的 XML 文件。首先,单击Save XML打开文件保存对话框。选择要使用的文件名,TDEditor 将为您生成一个 XML 文件。然后,您可以按照本文后面所述,将该 XML 用作 TDXML 的输入。

添加文本和图标

“文本和图标”页面用于设置任务对话框的静态文本元素。带有复选框的元素是可选的;如果一个元素未选中,它将不会出现在任务对话框中。例外的是Caption元素——如果您取消选中Caption,Windows 将提供一个默认标题。Vista 中的默认标题文本是显示对话框的可执行文件的名称。

为了演示编辑器的实际操作,我将展示如何构建一个类似于我之前关于使用 TaskDialogIndirect 的文章中的对话框。设置基本文本元素后,编辑器看起来如下:

到目前为止,任务对话框看起来是这样的:

请注意,页脚文本包含一个<a>标签,但它没有显示为超链接。暂时不用担心,我们稍后会修复这个问题。

添加推送按钮

“按钮”页面允许您指定要在任务对话框中显示的按钮。您可以添加内置的六个按钮中的任何一个,也可以添加任意数量的自定义文本按钮。您还可以选择自定义按钮显示为常规推送按钮还是命令链接。

对于我们的示例对话框,我们将添加一个“关闭”按钮和两个自定义按钮。使用Custom buttons列表右侧的按钮来添加、删除或编辑对话框的自定义按钮。添加或编辑自定义按钮时,TDEditor 会显示一个对话框,您可以在其中输入按钮的详细信息。

如果您使用命令链接(例如我们在本示例中所做的),您可以在Line 2 text编辑框中输入其他文本。此文本将显示在按钮的第二行,字体较小。

您可以将 ID 更改为任何值,但请注意,小数字可能会与内置按钮的 ID 冲突。默认 ID 为 1000,但任何大于 100 的数字都可以正常工作。TDEditor 会自动为每个附加按钮递增 ID,您还可以单击编辑器中的Reset IDs按钮为所有自定义按钮重新分配顺序 ID。

这是显示我们想要的按钮的任务对话框的“按钮”页面。

这是带有新按钮的任务对话框。

添加单选按钮

“单选按钮”页面与“按钮”页面的工作方式类似。由于没有内置单选按钮,此页面仅包含一个Custom buttons列表和相关控件。“默认按钮”设置的工作方式也略有不同——它控制对话框首次显示时哪个单选按钮将被选中。如果您不希望任何单选按钮一开始就被选中,请取消选中Default button设置。

我们虚构的 AntiFluff 程序使用的这个任务对话框通常不会有单选按钮,但为了说明该功能,我将添加两个按钮,如下所示:

这是任务对话框。请注意,第二个单选按钮已根据编辑器中的Default button设置被选中。

设置对话框标志

“标志”页面很简单,允许您切换一些任务对话框选项并设置一些附加属性。跑马灯进度条样式还有一个计时器设置,默认为 50 毫秒。这是进度条更新之间的时间量;值越小,跑马灯移动速度越快。

这是我们将为示例任务对话框设置的标志。

这是任务对话框的最终版本。

请注意,页脚现在包含一个超链接,因为我们设置了Allow hyperlinks标志。

使用 TDXML 显示任务对话框

您与 TDXML 库的接口是 CTaskDialogXML 类,它位于TaskDialogXML.cppTaskDialogXML.h文件中。使用 CTaskDialogXML 只需两个步骤:

  1. 通过告知对话框在哪里查找描述对话框内容的 XML 来初始化对话框。
  2. 如果初始化成功,请调用 DoModal() 来显示对话框。

有三种初始化对话框的方法,每种方法从不同的源读取 XML 数据:

bool InitFromFile ( LPCTSTR szFilename )
调用 InitFromFile() 使用 XML 文件初始化对话框。szFilename 是文件的完整路径。
bool InitFromResource ( resourceID, resourceType, hModule )
调用 InitFromResource() 使用存储在资源中的数据初始化对话框。resourceID 可以是数字 ID 或字符串 ID。resourceType 默认为“TDXML”;您可以将 XML 数据文件放在此自定义资源类型中,以便在二进制文件的资源中将它们分组在一起。hModule 是包含资源的 HMODULE。如果未指定 hModule,则使用当前进程的可执行文件。
bool InitFromVariant ( const _variant_t variantData )
InitFromVariant() 是其他两个初始化方法调用的函数,但如果数据源是 VARIANT_variant_t,您可以直接调用它。可以传递给 IXMLDOMDocument::load() 的任何数据都可以接受。

CTaskDialogXML 使用回调函数来实现一些任务对话框 API 中未内置的功能,例如带有 UAC 盾牌的按钮。这意味着,如果您需要使用自己的回调函数,则必须设置 CTaskDialogXML 对象的两个成员,它们等效于 TASKDIALOGCONFIGpfCallbacklpCallbackData 成员。这些成员是 m_pfCallbackm_lpCallbackData;当您设置它们时,CTaskDialogXML 将像 TaskDialogIndirect() 一样调用您的回调。

初始化 CTaskDialogXML 对象后,调用 DoModal() 来显示对话框。

HRESULT DoModal ( HWND hwndParent )
如果对话框未初始化或初始化失败,DoModal() 返回 E_FAIL,否则返回 TaskDialogIndirect() 的返回值。

如果 DoModal() 返回成功的 HRESULT,您可以读取 CTaskDialogXML 对象的三个公共成员,以查看用户在对话框中的操作:

m_nButton
用户单击以关闭对话框的推送按钮的 ID。
m_nRadioButton
关闭对话框时选中的单选按钮的 ID。
m_bChecked
一个 BOOL,指示对话框关闭时复选框的状态。

如果对话框中未包含相应的 UI 元素,则后两个成员将不包含有意义的数据。有关这些值如何设置的更多详细信息,请参阅 TaskDialogIndirect() 的文档。

可下载代码中的TDXMLTester项目包含三个任务对话框,并演示了使用 CTaskDialogXML 的各种方法。第一个对话框使用名为testdialog.xml的外部文件,该文件必须与TDXMLTester.exe位于同一目录中。源代码下载中包含一个示例文件,如果您愿意,也可以创建并使用自己的文件。

这是 TDXMLTester 使用外部 XML 文件的代码。

void CMainDlg::OnShowDialog1 (
  UINT uCode, int nID, HWND hwndCtrl )
{
HRESULT hr;
tdxml::CTaskDialogXML dlg;
TCHAR szTestFilename[MAX_PATH];
 
  GetModuleFileName ( NULL, szTestFilename,
                      _countof(szTestFilename) );
  PathRemoveFileSpec ( szTestFilename );
  PathAppend ( szTestFilename, _T("testdialog.xml") );
 
  if ( dlg.InitFromFile ( szTestFilename ) )
    hr = dlg.DoModal ( m_hWnd );
}

如您所见,没什么复杂的。代码构建 XML 文件的完整路径,然后调用 InitFromFile() 并传递该路径。如果 InitFromFile() 成功,代码将调用 DoModal()

其他两个对话框使用包含在TDXMLTester.exe中的资源的 XML。如果您查看 TDXMLTester 资源,您会发现在“TDXML”类型下有两个节点。

Test dialog 2 使用 IDR_CHNXML 资源,如下所示:

void CMainDlg::OnShowDialog2 (
  UINT uCode, int nID, HWND hwndCtrl )
{
HRESULT hr;
tdxml::CTaskDialogXML dlg;
 
  if ( dlg.InitFromResource ( IDR_CHNXML ) )
    hr = dlg.DoModal ( m_hWnd );
}

最后一个对话框非常相似,它只是使用字符串资源 ID 而不是数字 ID。Dialog 3 还演示了如何使用自定义回调函数来处理用户单击超链接时发送的通知。

void CMainDlg::OnShowDialog3 (
  UINT uCode, int nID, HWND hwndCtrl )
{
HRESULT hr;
tdxml::CTaskDialogXML dlg;
 
  if ( dlg.InitFromResource ( _T("BIGTESTDIALOG") ))
    {
    // This dialog has a hyperlink, so we need to set
    // up a callback function that launches the URL
    // associated with the link.
    dlg.m_pfCallback = TDCallback;
    dlg.m_lpCallbackData = (LONG_PTR) this;
 
    hr = dlg.DoModal ( m_hWnd );
    }
}

CTaskDialogXML 的其他详细信息

CTaskDialogXML 包含一个公共成员 m_td,它是传递给 TaskDialogIndirect()TASKDIALOGCONFIG 结构。您可以在调用 DoModal() 之前修改此结构,以执行任何其他初始化或设置步骤。

CTaskDialogXML::DoModal() 有两种调用 TaskDialogIndirect() 的方式。默认行为是直接调用 API,这要求您的应用程序与 Vista 版本的comctl32.dll静态链接。如果您不想有此依赖项(例如,您希望您的程序也能在 XP 上运行),可以在stdafx.h中定义 TDXML_DONT_LINK_TO_API 符号。这使得 DoModal() 使用 GetProcAddress() 获取 TaskDialogIndirect() 的地址。请注意,此方法仍然要求您的应用程序将comctl32.dll加载到其进程中,但如果操作系统不支持任务对话框,DoModal() 将只返回 E_FAIL

Bug 和功能待办列表

以下是我(我知道的!)的 Bug 以及我未来的一些功能设想。如果您有任何其他功能请求或 Bug 报告,请随时在此文章的论坛上发帖。

在任务对话框编辑器中:

  • 使用Reset IDs命令时,无法设置按钮的基 ID。
  • 编辑器不支持加载先前生成的 XML 文件。
  • 标准的对话框导航键不起作用(Tab、Alt+字母等)。

在 TDXML 库中:

  • 用于构建任务对话框的 XML 不支持加载字符串或图标资源。您可以通过在调用 DoModal() 之前修改 m_td 成员来解决此限制。
  • 修复使用该类与 MFC 相关的任何问题。
  • 默认加载资源的模块应根据应用程序是使用 MFC 还是 ATL 而有所不同。目前,它始终使用 NULL,这是 API 级别的默认值——有关更多信息,请参阅 FindResource() 文档。

进一步阅读

Vista 中的 C++ 实用功能:使用任务对话框显示友好消息

Vista 中的 C++ 实用功能:使用 TaskDialogIndirect 构建获取用户输入的对话框

面向开发者的 Windows Vista - 第二部分 - 任务对话框详解。Kenny Kerr 发表了一篇很长的文章,讨论了任务对话框的更多功能,以及大量的示例代码和一个 ATL 包装类。

MSDN 上有关于新的 Vista UI 指南的冗长页面。对于任务对话框,需要熟悉的是文本和语气页面。

版权和许可

本文是版权材料,©2007 Michael Dunn,保留所有权利。我知道这不会阻止人们在网上到处复制它,但我还是必须说。如果您有兴趣翻译本文,请给我发邮件告知。我不认为会拒绝任何人翻译的许可,只是想知道翻译的情况,以便在此发布链接。

本文附带的演示代码已发布到公共领域。我以这种方式发布代码是为了让代码造福所有人。(我不将文章本身设为公共领域,因为只在 CodeProject 上提供文章有助于提高我的知名度和 CodeProject 网站的流量。)如果您在自己的应用程序中使用演示代码,告知我一声将不胜感激(仅仅是满足我对人们是否从我的代码中受益的好奇心),但这不是必需的。在您自己的源代码中注明出处也受欢迎,但并非必需。

修订历史

2007 年 3 月 19 日:文章首次发布。

© . All rights reserved.