在 MFC 中创建嵌入式对话框






4.70/5 (32投票s)
2003年6月26日
6分钟阅读

258556

6428
使用 DS_CONTROL 在 MFC 中创建嵌入式对话框
引言
最近我们遇到了一个问题,需要在单个属性页或对话框中包含多个对话框。本文将解释我们是如何做到的。
从公司内部流传的讨论来看,这个功能是无休止的混乱和重写的根源。互联网上的各种示例也没有多大帮助,因为它们要求为 CPropertyPage
和 CDialog
类进行自定义派生,并且没有提供良好的键盘用户体验。
本文档记录了使用 MFC 在对话框中提供嵌入式对话框所需的步骤。没有提供扩展类,因为这提供了修改代码以支持嵌入式对话框的技术见解。
背景
将对话框嵌入到属性表或其他对话框中似乎是多年来许多困惑和各种尝试的根源。遗憾的是,我无法找到一个透明且干净的解决方案。网上有各种各样的解决方案,但没有一个具有正确解决方案的外观和感觉。然后我偶然发现了 Microsoft 网站上的文章“Q131283 - PRB: Cannot Use TAB to Move from Standard Controls to Custom”,一切都变得清晰了。
我们需要的解决方案必须简单(它只在一个对话框上),易于使用(对最终用户而言),易于维护(公司中并非所有人都是高手)。选择的解决方案是使用一个选项卡条,然后为每个页面使用 CDialog
。当选择当前页面时,前一个页面被隐藏,新页面被显示。
虽然这看起来很简单,易于记录,但实施起来却更具挑战性,尤其是在使用键盘进行 Tab 切换方面,这是产品的组成部分。用户需要能够通过 Tab 键进入和退出对话框,同时对话框在主窗口中看起来应该是整体的。
这种设计还允许开发人员使用通用控件 API 在编译时和运行时通过指定图像和选项卡名称等选项来设置选项卡控件。
使用代码
在代码部分,我给出了一个示例,展示了程序员如何在新的对话框中实现两个嵌入式对话框。这些对话框可以通过单击或导航到选项卡控件来选择。请注意,您可以将焦点设置到选项卡控件,然后使用左右箭头选择适当的选项卡,然后使用 Tab 键进入和退出对话框。
首先要做的是创建将嵌入到主对话框中的对话框。这些只是两个直截了当的 CDialog
类。您可以在其中放置任何您喜欢的控件。唯一的要求是,在资源编辑器中对话框属性的“更多样式”选项卡上,对话框样式设置为 Child 和 Control。
建议将框架类型设置为对话框框架。此外,您可以设置对话框相对于其父级的 X 和 Y 位置。要嵌入的对话框应偏移,以便对话框框架顶部显示为选项卡框架底部。当然,这取决于个人喜好和美学。
在示例中,已经创建了两个对话框。文件 Dialog1.h 和 Dialog1.cpp 中的 CDialog1
以及 Dialog2.h 和 Dialog2.cpp 中的 CDialog2
。
现在我们已经构建了要嵌入到主对话框中的两个对话框,我们可以创建主对话框了。
我们创建了一个名为 CEmbeddedDialogDlg
的新对话框类,在 EmbeddedDialogDlg.h 和 EmbeddedDialogDlg.cpp 两个文件中实现。嵌入式对话框的实现将在此处进行。
创建对话框后,我们需要为我们之前创建的两个 CDialog
类添加包含,并将它们作为成员添加到 CEmbeddedDialogDlg
类。这些是将插入到 CEmbeddedDialogDlg.h 类中的无模式对话框。
public:
CDialog2 m_dlg2;
CDialog1 m_dlg1;
接下来,我们将一个新的选项卡控件资源插入到 CEmbeddedDialog
类的对话框资源中,并子类化此控件,以便于操作。这被添加为 CEmbeddedDialogDlg
类的成员 m_ctlTab1
。
CEmbeddedDialogDlg
类中唯一需要的功能是 UpdateVisibleWindow
方法,它显示与当前选项卡关联的对话框并隐藏所有其他对话框。当用户更改选项卡控件中的选项卡时,因为它是一个 Windows 通用控件,所以它会生成一个 WM_NOTIFY
消息,其中包含 TCN_SELCHANGE
子消息,表示当前选项卡选择已更改。我们可以使用 CTabCtrl::GetCurSel()
获取当前选项卡选择。这相当于将发送到本机控件的 TCM_GETCURSEL
消息。MFC 只是使其更容易。
拼图的最后一块是 CEmbeddedDialogDlg::OnInitDialog
成员。这是在对话框首次构造但尚未显示之前首次调用的方法。主要的更改部分发生在此处。
此方法的第一步是创建我们将使用的两个选项卡。
m_ctlTab1.InsertItem(TCIF_TEXT, 0, _T("Dialog1"), 0,0,0,0); m_ctlTab1.InsertItem(TCIF_TEXT, 1, _T("Dialog2"), 0,0,0,0);
接下来,我们创建两个对话框的无模式实例,如下所示。
m_dlg1.Create(CDialog1::IDD, this); m_dlg2.Create(CDialog2::IDD, this);
请注意,我们将两个对话框实例的父级设置为 CEmbeddedDialogDlg
对话框,因此所有偏移都将相对于 CEmbeddedDialogDlg
实例。
现在,如果您调用 UpdateVisibleWindow()
并运行项目,您将看到项目正常工作,并且您可以选择不同的选项卡以及使用选项卡控件在对话框之间切换。但是,您会发现选项卡顺序似乎不正确,因为 Tab 切换会将您带到选项卡控件,然后到确定和取消按钮,然后再到嵌入式控件。显然这是不正确和令人困惑的,所以我们需要更改新插入控件的选项卡顺序,使它们在选项卡控件之后正确地进行 Tab 切换。
m_dlg2.SetWindowPos(GetDlgItem(IDC_TAB1), 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE); m_dlg1.SetWindowPos(GetDlgItem(IDC_TAB1), 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
为此,我们使用 SetWindowPos 函数并告诉窗口它在选项卡顺序中的位置。请注意,我们已经颠倒了 SetWindowPos
调用的顺序,第一个调用是 CDialog2
,然后下一个调用是 CDialog1
。这使得代码更容易理解,没有理由不能遵循其他实现。
最后一点需要注意的是,即使它不在代码中,您也应该在 CEmbeddedDialogDlg::DoDataExchange
中调用 m_dlg1
和 m_dlg2
的 DoDataExchange
方法。
关注点
一旦理解了 DS_CONTROL
样式,我惊讶于实现嵌入式对话框是多么容易。整个模型在 Windows 框架内简单轻松地集成,提供了 Microsoft 嵌入式对话框的预期路线的外观和感觉。
历史
根据讨论,我展示了如何添加对 XP 主题的支持。
添加主题支持是一个两步操作。第一步是创建清单文件,可以作为资源嵌入,也可以是带有 yourapp.exe.manifest 文件名的单独文件。如果两者都存在并且您正在运行 XP,则应用程序将采用当前 XP 主题的主题。
第二步是使选项卡与背景颜色相同。这可以通过调用 EnableThemeDialogTexture
并使用 ETDT_ENABLETAB
标志来实现。显然,此可执行文件现在只能在 XP 上运行。
我在 .dsp 文件中添加了两个附加配置,一个名为 Win32 XP Debug,另一个名为 Win32 XP Release。有关主题的优秀文章,请参阅 Microsoft 网站上的使用 Windows XP 视觉样式。