简体   繁体   English

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

[英]Embedding modeless dialogs as child window with Windows API

I know, there must be a way to embed an (modeless) dialog as child of a window created with CreateWindow . 我知道,必须有一种方法可以将(无模式)对话框作为使用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). 就我而言,我想将它们嵌入可滚动显示的容器窗口中,由此,该容器窗口本身就是主窗口的子级(参见图片)。

嵌入式对话框

The first problem that I encounter is, that I still want be able to use TAB keys and other dialog specific navigation. 我遇到的第一个问题是,我仍然希望能够使用TAB键和其他对话框特定的导航。 But how? 但是如何?

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. 如果删除了IsDialogMessage则可以在其中一个嵌入式对话框中的编辑控件中输入一些文本,但是对话框导航无法正常工作。 Of course, WS_TABSTOP style is set for dialog child controls. 当然,为对话框子控件设置了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, ...) ). 我的GroupBarPanelInternalAddLast通过删除标题和边框GroupBarPanelInternalAddLast修改无模式对话框样式,并确保设置了WS_CHILDWS_VISIBLEWS_TABSTOPSetWindowLong(hWndDlg, GWL_STYLE, ...) )。 (Also tested WS_EX_CONTROLPARENT style) With SetParent(hWndDlg, hWndContainer) modeless dialogs parent is changed. (还测试了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_SETFOCUSWM_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);
    }
}

You are not using IsDialogMessage() correctly, and you said you learned from some bad tutorials. 您没有正确使用IsDialogMessage() ,并说您从一些错误的教程中学到了东西。 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() takes two parameters: the message itself, and the window handle of the toplevel window . IsDialogMessage()具有两个参数:消息本身和顶级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. 粗体字的这一部分是重要的组成部分:在选项卡导航或Enter / Esc处理的情况下, IsDialogMessage()函数需要知道要使用哪个对话框。

You don't want to pass msg.hwnd ; 你不想通过msg.hwnd ; that's the control itself. 那就是控件本身。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM