I know, there must be a way to embed an (modeless) dialog as child of a window created with CreateWindow
. In my case I want to embed them into an scroll-able container window, whereby this container windows it self is a child of the main window (see picture).
我知道,必须有一种方法可以将(无模式)对话框嵌入到使用CreateWindow创建的窗口的子对象中。在我的情况下,我想将它们嵌入到一个可滚动的容器窗口中,这个容器窗口自己是主窗口的子窗口(见图)。
The first problem that I encounter is, that I still want be able to use TAB keys and other dialog specific navigation. But how?
我遇到的第一个问题是,我仍然希望能够使用TAB键和其他对话框特定的导航。但是怎么样?
My message loop:
我的消息循环:
while (GetMessage(&msg, NULL, 0, 0)) {
if (IsDialogMessage(msg.hwnd, &msg)) continue;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Edit: For testing purpose I modified the loop:
编辑:出于测试目的,我修改了循环:
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);
}
}
More information how to make it the right way can be found here: Using the TAB key to navigate in non-dialogs, redux
可以在此处找到有关如何使其正确使用的更多信息:使用TAB键在非对话框中导航,redux
With this message loop, nothing happens if I want to enter some dialog texts as if the message is not handled. If IsDialogMessage
is removed, I can enter some text into an edit control in one of the embedded dialogs, however dialog navigation doesn't work as intended. Of course, WS_TABSTOP
style is set for dialog child controls.
使用此消息循环,如果我想输入一些对话文本,就好像未处理消息一样,没有任何反应。如果删除了IsDialogMessage,我可以在其中一个嵌入式对话框中的某个编辑控件中输入一些文本,但是对话框导航不能按预期工作。当然,为对话框子控件设置了WS_TABSTOP样式。
The scroll-able container is created with CreateWindowEx
with styles WS_CHILD, WS_VISIBLE, WS_VSCROLL, WS_TABSTOP, WS_EX_CONTROLPARENT
and dialogs are created as children of this container.
使用CreateWindowEx创建可滚动容器,其样式为WS_CHILD,WS_VISIBLE,WS_VSCROLL,WS_TABSTOP,WS_EX_CONTROLPARENT,并创建对话框作为此容器的子项。
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);
}
My GroupBarPanelInternalAddLast
modify modeless dialog styles by removing caption and borders, and ensures that WS_CHILD
, WS_VISIBLE
and WS_TABSTOP
is set (SetWindowLong(hWndDlg, GWL_STYLE, ...)
). (Also tested WS_EX_CONTROLPARENT
style) With SetParent(hWndDlg, hWndContainer)
modeless dialogs parent is changed.
我的GroupBarPanelInternalAddLast通过删除标题和边框来修改无模式对话框样式,并确保设置WS_CHILD,WS_VISIBLE和WS_TABSTOP(SetWindowLong(hWndDlg,GWL_STYLE,...))。 (还测试了WS_EX_CONTROLPARENT样式)使用SetParent(hWndDlg,hWndContainer)无模式对话框更改父级。
So what am I missing here? As I found out, neither the container window procedure nor the embedded (for testing purpose subclassed) dialog procedure almost never gets WM_SETFOCUS
or WM_KILLFOCUS
messages for example, but why that?
那我在这里错过了什么?正如我所发现的那样,容器窗口过程和嵌入式(用于测试目的子类)对话程序几乎都不会获得WM_SETFOCUS或WM_KILLFOCUS消息,但为什么呢?
Solution: Calling IsDialogMessage for the top level window.
解决方案:为*窗口调用IsDialogMessage。
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
if (IsDialogMessage(hWndTopLevel, &msg)) {
continue;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
1 个解决方案
#1
2
You are not using IsDialogMessage()
correctly, and you said you learned from some bad tutorials. I don't know which tutorials you saw, so I can't tell you what they got wrong; all I can do is say how to use it correctly.
你没有正确使用IsDialogMessage(),你说你从一些不好的教程中学到了什么。我不知道你看过哪些教程,所以我不能告诉你他们错了什么;我所能做的只是说明如何正确使用它。
IsDialogMessage()
takes two parameters: the message itself, and the window handle of the toplevel window. This bit in bold is the important part: the IsDialogMessage()
function needs to know which dialog to work with in the event of tab navigation or Enter/Esc handling.
IsDialogMessage()接受两个参数:消息本身和顶层窗口的窗口句柄。这个粗体位是重要部分:IsDialogMessage()函数需要知道在选项卡导航或Enter / Esc处理时使用哪个对话框。
You don't want to pass msg.hwnd
; that's the control itself.
你不想传递msg.hwnd;这就是控制本身。
And in the case of nested child dialogs like you have here, you don't want to pass in the child dialog's handle; that would confine IsDialogMessage()
to that dialog.
对于像这里一样的嵌套子对话框,你不想传入子对话框的句柄;这会将IsDialogMessage()限制在该对话框中。
So in your screenshot, you want to pass in the handle of the main window, that is, the window called Win32ApiDemo1
.
因此,在屏幕截图中,您希望传递主窗口的句柄,即名为Win32ApiDemo1的窗口。
Also make sure all the child dialogs and your custom expander control have WS_EX_CONTROLPARENT
so tab navigation can recurse.
还要确保所有子对话框和自定义扩展器控件都具有WS_EX_CONTROLPARENT,因此选项卡导航可以递归。
#1
2
You are not using IsDialogMessage()
correctly, and you said you learned from some bad tutorials. I don't know which tutorials you saw, so I can't tell you what they got wrong; all I can do is say how to use it correctly.
你没有正确使用IsDialogMessage(),你说你从一些不好的教程中学到了什么。我不知道你看过哪些教程,所以我不能告诉你他们错了什么;我所能做的只是说明如何正确使用它。
IsDialogMessage()
takes two parameters: the message itself, and the window handle of the toplevel window. This bit in bold is the important part: the IsDialogMessage()
function needs to know which dialog to work with in the event of tab navigation or Enter/Esc handling.
IsDialogMessage()接受两个参数:消息本身和顶层窗口的窗口句柄。这个粗体位是重要部分:IsDialogMessage()函数需要知道在选项卡导航或Enter / Esc处理时使用哪个对话框。
You don't want to pass msg.hwnd
; that's the control itself.
你不想传递msg.hwnd;这就是控制本身。
And in the case of nested child dialogs like you have here, you don't want to pass in the child dialog's handle; that would confine IsDialogMessage()
to that dialog.
对于像这里一样的嵌套子对话框,你不想传入子对话框的句柄;这会将IsDialogMessage()限制在该对话框中。
So in your screenshot, you want to pass in the handle of the main window, that is, the window called Win32ApiDemo1
.
因此,在屏幕截图中,您希望传递主窗口的句柄,即名为Win32ApiDemo1的窗口。
Also make sure all the child dialogs and your custom expander control have WS_EX_CONTROLPARENT
so tab navigation can recurse.
还要确保所有子对话框和自定义扩展器控件都具有WS_EX_CONTROLPARENT,因此选项卡导航可以递归。