对话框管理器第二章:创建框架窗口
来源:http://www.tudoupe.com时间:2022-07-25
对话框模板包含了对话框外观的描述,所以对话框管理器只是简单地遍历模板并按照模板的描述来创建对话框。这个过程十分简单和直接,对话框管理器没有太多自己的决策空间,它只是按照模板说的做而已。
为了简化,我假设我所讨论的对话框管理器是对话框模板的一个扩展版本,它是一个经典的DLGTEMPLATE超集,因此我将更一般地介绍它。
此外,我将省略一些更多的特殊部分(例如WM_ENTERIDLE消息),因为它们与我将做的主要部分没什么关系。
同时, 由于 范围, 我 也 会 稍微 处理 这些 错误 。
最后,我假设你已经理解了各种对话框模板的结构,并且忽略了模板语句分析的问题。
好了,可以开始了。
第一个任务是研究对话框模式,并转换DS_*模式到WS_*和WS_EX_*模式,如下图所示:

问题:为什么定义的DS_CONTROL风格会导致WS_CAPTION和WS_SYSMENU被删除?
回答:通过简单的添加样式标记,更容易将现有对话框转换为DS_CONTROL子对话框。
如果对话框模板包含菜单,则从创建参数的一部分传递的示例手柄中载入菜单。
hmenu = LoadMenu(hinst, );
这是一个在对话创建中常见的方法:通过对话创建函数传递的实例处理器在对话创建期间用于所有与资源有关的活动。
获取对话框字体的算法如下:

注意DS_SETFONT比DS_FIXEDFONT更佳。
一旦对话框管理员有一个字体,就会对其进行测量,转换对话框单元(DLUs)到像素以使用它们的大小。对话框布局中的所有内容都在DLU中完成。如果你忘了把DLU转换成像素公式,这里有个提醒。 具体公式如下:
// 4 xdlu = 1 average character width
// 8 ydlu = 1 average character height
#define XDLU2Pix(xdlu) MulDiv(xdlu, AveCharWidth, 4)
#define YDLU2Pix(ydlu) MulDiv(ydlu, AveCharHeight, 8)
对话框大小来自模板:
cxDlg = XDLU2Pix(DialogTemplate.cx);
cyDlg = YDLU2Pix(DialogTemplate.cy);
模板中的对话框大小是客户端区域的大小,所以我们还需要将其添加到非客户端区域。
RECT rcAdjust = { 0, 0, cxDlg, cyDlg };
AdjustWindowRectEx(&rcAdjust, dwStyle, hmenu != NULL, dwExStyle);
int cxDlg = rcAdjust.right – rcAdjust.left;
int cyDlg = rcAdjust.bottom – rcAdjust.top;
我怎么知道它是客户区而不是包括非客户区在内的整个窗口? 因为如果是全窗矩形,就不可能设计对话框! 模板设计者不知道最终用户的系统将设置为哪些非客户端指标,因此无法在设计时考虑它。
(这是一个更一般的规则的例子:如果你不确定某事是否真的,问问自己,“如果它是真实的,世界会是什么样子?如果你发现一个明显错误的逻辑结果,所以你只是[用矛盾]证明你的想法真的不是真的。这是一个重要的逻辑原则,我会再来再来。事实上,你几天前看见了。)
假设DS_ABSALIGN样式没有设置,对话框模板中给出的坐标与对话框的父相对。
POINT pt = { XDLU2Pix(DialogTemplate.x),YDLU2Pix(DialogTemplate.y) };
ClientToScreen(hwndParent, &pt);
但是如果调用者通过 IdParent = NULL 怎么办?在这种情况下,对话框的位置是主屏幕左上角,但不要这样做。
> 在多显示器系统上,它会将对话框放在主显示器上,即使你的程序正在辅助显示器上运行。
> 用户可能将他们的任务栏停靠在屏幕的顶部或左侧边缘,这将覆盖你的对话框。
> 即使在单显示器系统上,你的程序也可能在屏幕的右下角运行。 将对话放在左上角不会在两者之间建立有意义的联系。
> 如果你的程序的两个副本正在运行,它们的对话框将精确地相互覆盖。 我们在之前的文章中看到了这种情况的危险。
故事的寓言:总是通过窗口 towndParent,以便对话框在与其他程序有关的有意义的位置出现。 (而且不要只是抓住 GetDesktopWindow!)
好的,我们现在准备好创建对话框:我们有它的类,它的字体,它的菜单,它的大小和它的位置,等等。
等等,我们必须处理我们前面讨论的对话创建的微妙之处:对话总是在开头隐藏。
BOOL fWasVisible = dwStyle & WS_VISIBLE;
dwStyle &= ~WS_VISIBLE;
对话框类别和标题来自模板。几乎每个人都只使用默认的对话类,虽然我在前面的文章中解释了如何使用自定义对话类。
好吧,现在我们有了创建窗口所需的信息。
HWND hdlg = CreateWindowEx(dwExStyle, pszClass,
pszCaption, dwStyle & 0xFFFF0000, pt.x, pt.y,
cxDlg, cyDlg, hwndParent, hmenu, hinst, NULL);
请注意,由于我们将DS_*模型转换为“真实”模型,我们已将所有低模型位置(每个类)筛选出来。
这就是为什么你的对话过程没有得到像 WM_CREATE 这样的窗口创建消息的原因。 在创建框架时,对话过程还没有进入画面。 只有在框架创建之后,对话管理器才能附加对话过程。
// Set the dialog procedure
SetWindowLongPtr(hdlg, DWLP_DLGPROC, (LPARAM)lpDlgProc);
对话框管理器在这一点上做了更多的摆弄,基于对话框模板样式。 模板可能要求提供窗口上下文帮助 ID。 如果模板没有指定允许调整大小、最大化或最小化的窗口样式,则相关的菜单项将从对话框的系统菜单中删除。
接下来是设置字体:
SetWindowFont(hdlg, hf, FALSE);
这就是为什么你的对话过程收到的第一条消息恰好是 WM_SETFONT:它是在设置 DWLP_DLGPROC 之后发送的第一条消息。 当然,这种行为将来可能会改变; 你不应该依赖消息排序。
好的, 对话框已经创建. 下一步: 创建控件.
总结
拖移控制非常简单,但操作系统意识到其基本原理并不那么简单。
你可以:
> 忽略这些复杂的,繁琐的东西,继续”接着奏乐接着舞”。
> 苦其心志,深入研究这些细枝末节的东西。
嗯, 选择是你方的.
最后
雷蒙德·陈的《旧新事物》(The Old New Thing)是我最喜欢的博客之一,它包含了很多关于Windows的小信息,这对很多Windows平台开发者很有帮助。
本文来自:《The dialog manager, part 2: Creating the frame window》

相关新闻
- 2022-08-04 WPF的由来
- 2022-08-04 Win11勒索软件防护怎么打开?Win11安
- 2022-08-04 Windows系统jdk的配置
- 2022-08-04 Windows10 OneNote怎么重新登录?如何重
- 2022-08-04 超好用的 Windows 效率工具推荐
- 2022-08-04 Windows如何在CMD或PowerShell中配置代理
- 2022-08-04 powershell和cmd对比
- 2022-08-04 【QT】Windows下QT下载安装
- 2022-08-04 windows下 C++ 实现类属性的get和set方
- 2022-08-04 Win11快速助手在哪里?Win11打开快速
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
