使用Windows API将无模式对话框嵌入为子窗口

时间:2021-04-17 07:30:17

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创建的窗口的子对象中。在我的情况下,我想将它们嵌入到一个可滚动的容器窗口中,这个容器窗口自己是主窗口的子窗口(见图)。

使用Windows API将无模式对话框嵌入为子窗口

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)无模式对话框更改父级。

使用Windows API将无模式对话框嵌入为子窗口

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,因此选项卡导航可以递归。