【问题标题】:Embedding modeless dialogs as child window with Windows API使用 Windows API 将无模式对话框嵌入为子窗口
【发布时间】:2016-10-31 17:17:46
【问题描述】:

我知道,必须有一种方法可以将(非模式)对话框嵌入为使用CreateWindow 创建的窗口的子窗口。就我而言,我想将它们嵌入到一个可滚动的容器窗口中,而这个容器窗口本身就是主窗口的子窗口(见图)。

我遇到的第一个问题是,我仍然希望能够使用 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 样式是为对话框子控件设置的。

可滚动容器是使用 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);
    }

我的GroupBarPanelInternalAddLast 通过删除标题和边框来修改无模式对话框样式,并确保设置了WS_CHILDWS_VISIBLEWS_TABSTOP (SetWindowLong(hWndDlg, GWL_STYLE, ...))。 (还测试了WS_EX_CONTROLPARENT 样式)随着SetParent(hWndDlg, hWndContainer) 无模式对话框的父级已更改。

那么我在这里错过了什么?正如我发现的那样,容器窗口过程和嵌入式(用于测试目的的子类化)对话框过程几乎都不会收到 WM_SETFOCUSWM_KILLFOCUS 消息,但为什么会这样呢?

解决方案:为顶层窗口调用 IsDialogMessage。

while (GetMessage(&msg, NULL, 0, 0)) {
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
        if (IsDialogMessage(hWndTopLevel, &msg)) {
            continue;
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

【问题讨论】:

  • IsDialogMessage 需要传递候选对话框的HWND,而不是消息针对的窗口。
  • 嗯,它应该被赋予具有WS_EX_CONTROLPARENT 样式的窗口的HWND,它是其他对话框的父级。
  • 创建时需要手动设置输入焦点,否则键盘导航不起作用。我并不完全相信这可以可靠地工作。你为什么不创建常规窗口呢?
  • @JonathanPotter 它也没有用。我测试了这两个, IsDialogMessage 只为容器窗口调用,我还测试了为对话窗口调用它。两者都不起作用。
  • 呃哦,你的IsDialogMessage()电话还是错了。您不想仅在将消息发送到对话框时才调用IsDialogMessage()。您想为 all 消息调用它,传递包含子对话框的最顶层窗口的 HWND - 在您的情况下,是 Win32AppDemo1 窗口。

标签: c winapi dialog childwindow createwindow


【解决方案1】:

你没有正确使用IsDialogMessage(),你说你从一些糟糕的教程中学到了东西。我不知道你看到了哪些教程,所以我不能告诉你他们哪里错了;我能做的就是说出如何正确使用它。

IsDialogMessage() 有两个参数:消息本身和顶层窗口的窗口句柄。粗体部分是重要部分:IsDialogMessage() 函数需要知道在选项卡导航或 Enter/Esc 处理的情况下使用哪个对话框。

你不想通过msg.hwnd;这就是控件本身。

对于像这里这样的嵌套子对话框,您不想传入子对话框的句柄;这会将IsDialogMessage() 限制在该对话框中。

所以在你的截图中,你要传入主窗口的句柄,即名为Win32ApiDemo1的窗口。

还要确保所有子对话框和您的自定义扩展器控件都有WS_EX_CONTROLPARENT,以便标签导航可以递归。

【讨论】:

  • 准确地说,这个例子中的哪些窗口句柄应该有WS_EX_CONTROLPARENT 扩展样式?我尝试为每个窗口设置它,但它不会递归。
猜你喜欢
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多