使用CreateWindow创建Win32模态窗口

19

我使用 CreateWindow() 创建了一个窗口,然后用 ShowWindow() 显示它。但是,创建该窗口的父窗口应该在用户返回该窗口之前禁用,即应该模拟模态对话框。

5个回答

23

确保在CreateWindow中设置hwndParent并使用EnableWindow(hwndParent, FALSE)来禁用父窗口,以便在显示弹出窗口后禁用它。在弹出窗口关闭后,使用EnableWindow(hwndParent, TRUE)启用父窗口。


7
在弹出窗口关闭之前,您需要先调用 EnableWindow(hwndParent, TRUE) 以正确的顺序禁用和启用窗口,请参阅禁用和启用窗口的正确顺序 - Alexey Ivanov
@AlexeyIvanov 链接已失效,新链接为https://devblogs.microsoft.com/oldnewthing/20040227-00/?p=40463 - El Mismo Sol

14

2
Old New Thing 的参考资料很棒,但遗憾的是它只是一个链接答案。你可能需要用自己的话重新表述一些文章内容。 - jrh

4
你需要考虑模态窗口的含义 - 基本上,窗口的父窗口会被禁用。我所知道的唯一自动执行此操作的方法是调用 DialogBox() 来创建模态对话框。然而,如果你想使用 CreateWindow(),那么你需要手动禁用父窗口。
理想情况下,最好采用对话框方式(因为操作系统知道创建模态窗口所需的确切步骤),但如果必须使用它,则可以选择这个选项。

3

好的,我刚才自己也遇到了这个问题。我需要一个快速对话框,它的行为就像我使用DialogBox()时的那样,但我不想为我正在使用的特定项目创建一个模板。

我的发现是,如果禁用对话框的父窗口,则也会禁用对话框。而且你不能重新启用该对话框,而不重新启用父对话框。所以那种方法行不通。

我还发现,你不能使用SetCapture()/ReleaseCapture(),因为这样对话框的子窗口将无法收到消息。

我找到了一种解决方案:使用本地消息泵,由PeekMessage()或GetMessage()驱动。以下是适用于我的代码:

  while (!m_bFinished)
  {
     BOOL bEat;

     if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
     {
        if (msg.message == WM_CHAR)
        {
           if (msg.wParam == VK_ESCAPE)
           {
              m_bFinished = TRUE;
              continue;
           }
        }

        bEat = FALSE;

        if (msg.message >= WM_MOUSEFIRST &&
           msg.message <= WM_MOUSELAST)
        {
           RECT rectMe;

           pcMe->GetWindowRect(&rectMe);
           if (!::PtInRect(&rectMe, msg.pt))
              bEat = TRUE;
        }

        if (!bEat)
        {
           ::TranslateMessage(&msg);
           ::DispatchMessage(&msg);
        }
     }
  }

这实际上会“吃掉”所有在应用程序中传递的鼠标消息中位于窗口客户区之外的消息。它不会禁止在应用程序外单击,只是禁止在“模态”窗口的客户区之外的任何位置单击。如果在吞噬消息时添加MessageBeep(),则将获得与真正的模态对话框完全相同的行为。
m_bFinished是该类的BOOL成员,并且如果访问了“对话框”的确定或取消按钮,以及代码片段范围之外的某些其他条件,则会设置它。

3

您还可以运行一个“辅助消息循环”,使父窗口保持不活动,直到您完成与“模态”对话框的工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接