将无模式对话框嵌入为 child window 和 Windows API
Embedding modeless dialogs as child window with Windows API
我知道,必须有一种方法可以将(无模式)对话框嵌入为使用 CreateWindow
创建的 window 的 child。在我的例子中,我想将它们嵌入到一个 scroll-able 容器 window 中,其中这个容器 windows 它本身是主 window 的 child(见图).
我遇到的第一个问题是,我仍然希望能够使用 TAB 键和其他特定于对话框的导航。但是怎么办?
我的消息循环:
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
编辑: 出于测试目的,我修改了循环:
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsEmbeddedDialogWindow(msg.hwnd)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsScrollableContainerWindow(msg.hwnd)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
可以在此处找到有关如何以正确的方式实现的更多信息:Using the TAB key to navigate in non-dialogs, redux
使用这个消息循环,如果我想输入一些对话文本,就好像消息没有被处理一样,什么也不会发生。如果 IsDialogMessage
被删除,我可以在其中一个嵌入式对话框的编辑控件中输入一些文本,但是对话框导航无法按预期工作。当然,WS_TABSTOP
样式是为对话框 child 控件设置的。
scroll-able 容器是用 CreateWindowEx
创建的,带有样式
WS_CHILD, WS_VISIBLE, WS_VSCROLL, WS_TABSTOP, WS_EX_CONTROLPARENT
和对话框创建为该容器的 children。
HWND hWndContainer = GroupBarPanelCreate(WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, WS_EX_CONTROLPARENT, hWndMain, 0, 0, 400, 400);
GROUPBAR_PANEL* GroupBarPanel = (GROUPBAR_PANEL*) GetWindowLongPtr(hWndContainer, 0);
// Test embedding dialogs
for (unsigned int i = 0; i < 10; i++) {
HWND hWndDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWndContainer, About, 0);
GroupBarPanelInternalAddLast(GroupBarPanel, hWndChild, hWndDlg, nullptr);
}
我的 GroupBarPanelInternalAddLast
通过删除标题和边框修改无模式对话框样式,并确保设置 WS_CHILD
、WS_VISIBLE
和 WS_TABSTOP
(SetWindowLong(hWndDlg, GWL_STYLE, ...)
) . (还测试了 WS_EX_CONTROLPARENT
样式)使用 SetParent(hWndDlg, hWndContainer)
无模式对话框 parent 已更改。
那么我在这里缺少什么?正如我发现的那样,容器 window 过程和嵌入式(用于测试目的的子类)对话过程几乎都不会收到 WM_SETFOCUS
或 WM_KILLFOCUS
消息,但这是为什么呢?
解决方法: 为顶层调用 IsDialogMessage window.
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
if (IsDialogMessage(hWndTopLevel, &msg)) {
continue;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
你没有正确使用 IsDialogMessage()
,你说你从一些糟糕的教程中学到了东西。我不知道你看了哪些教程,所以我不能告诉你他们错了什么;我只能说如何正确使用它。
IsDialogMessage()
有两个参数:消息本身,以及 toplevel window 的 window 句柄。粗体部分是重要部分:IsDialogMessage()
函数需要知道在选项卡导航或 Enter/Esc 处理时要使用哪个对话框。
你不想通过msg.hwnd
;那就是控件本身。
并且在像您这里这样的嵌套子对话框的情况下,您不想传入子对话框的句柄;这会将 IsDialogMessage()
限制在该对话框中。
所以在你的截图中,你想传入主 window 的句柄,即调用 Win32ApiDemo1
的 window。
还要确保所有子对话框和您的自定义扩展器控件都具有 WS_EX_CONTROLPARENT
,以便选项卡导航可以递归。
我知道,必须有一种方法可以将(无模式)对话框嵌入为使用 CreateWindow
创建的 window 的 child。在我的例子中,我想将它们嵌入到一个 scroll-able 容器 window 中,其中这个容器 windows 它本身是主 window 的 child(见图).
我遇到的第一个问题是,我仍然希望能够使用 TAB 键和其他特定于对话框的导航。但是怎么办?
我的消息循环:
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
编辑: 出于测试目的,我修改了循环:
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsEmbeddedDialogWindow(msg.hwnd)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsScrollableContainerWindow(msg.hwnd)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
可以在此处找到有关如何以正确的方式实现的更多信息:Using the TAB key to navigate in non-dialogs, redux
使用这个消息循环,如果我想输入一些对话文本,就好像消息没有被处理一样,什么也不会发生。如果 IsDialogMessage
被删除,我可以在其中一个嵌入式对话框的编辑控件中输入一些文本,但是对话框导航无法按预期工作。当然,WS_TABSTOP
样式是为对话框 child 控件设置的。
scroll-able 容器是用 CreateWindowEx
创建的,带有样式
WS_CHILD, WS_VISIBLE, WS_VSCROLL, WS_TABSTOP, WS_EX_CONTROLPARENT
和对话框创建为该容器的 children。
HWND hWndContainer = GroupBarPanelCreate(WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, WS_EX_CONTROLPARENT, hWndMain, 0, 0, 400, 400);
GROUPBAR_PANEL* GroupBarPanel = (GROUPBAR_PANEL*) GetWindowLongPtr(hWndContainer, 0);
// Test embedding dialogs
for (unsigned int i = 0; i < 10; i++) {
HWND hWndDlg = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWndContainer, About, 0);
GroupBarPanelInternalAddLast(GroupBarPanel, hWndChild, hWndDlg, nullptr);
}
我的 GroupBarPanelInternalAddLast
通过删除标题和边框修改无模式对话框样式,并确保设置 WS_CHILD
、WS_VISIBLE
和 WS_TABSTOP
(SetWindowLong(hWndDlg, GWL_STYLE, ...)
) . (还测试了 WS_EX_CONTROLPARENT
样式)使用 SetParent(hWndDlg, hWndContainer)
无模式对话框 parent 已更改。
那么我在这里缺少什么?正如我发现的那样,容器 window 过程和嵌入式(用于测试目的的子类)对话过程几乎都不会收到 WM_SETFOCUS
或 WM_KILLFOCUS
消息,但这是为什么呢?
解决方法: 为顶层调用 IsDialogMessage window.
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
if (IsDialogMessage(hWndTopLevel, &msg)) {
continue;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
你没有正确使用 IsDialogMessage()
,你说你从一些糟糕的教程中学到了东西。我不知道你看了哪些教程,所以我不能告诉你他们错了什么;我只能说如何正确使用它。
IsDialogMessage()
有两个参数:消息本身,以及 toplevel window 的 window 句柄。粗体部分是重要部分:IsDialogMessage()
函数需要知道在选项卡导航或 Enter/Esc 处理时要使用哪个对话框。
你不想通过msg.hwnd
;那就是控件本身。
并且在像您这里这样的嵌套子对话框的情况下,您不想传入子对话框的句柄;这会将 IsDialogMessage()
限制在该对话框中。
所以在你的截图中,你想传入主 window 的句柄,即调用 Win32ApiDemo1
的 window。
还要确保所有子对话框和您的自定义扩展器控件都具有 WS_EX_CONTROLPARENT
,以便选项卡导航可以递归。