如何在WTL中模拟模态对话框?

时间:2016-11-13 21:32:21

标签: visual-c++ wtl

模态对话框很好用且易于使用。问题是他们不允许我自己处理消息循环。所以我想我也许可以使用一个无模式对话框来模拟一个模态对话框,并且仍然自己负责消息循环以便处理加速器。

目标

我想要达到的目的是能够按 Ctrl + C (以及 Ctrl + Ins )当对话框具有焦点时,我希望能够通过将一些信息复制到剪贴板来对此作出反应。因此,如果有人知道在WTL中使用模态对话框的方法,那也会回答我的问题。

我现在在做什么

现在我正在做的是从CDialogImpl<T>CMessageFilter派生我的对话类,以便让我负责PreTranslateMessage。在那里,我只需使用CAccelerator::TranslateAcceleratorCWindow::IsDialogMessage来处理加速器和对话框消息。

OnInitDialog中,我填充加速器表并将消息过滤器添加到(“全局”)消息循环中。加速器表与对话框本身具有相同的资源ID:

m_accel.Attach(AtlLoadAccelerators(IDD));
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->AddMessageFilter(this);

然后我使用名为DoModal的{​​{1}}创建了一个使用“全局”消息循环的代理。

现在我看到的效果(出现在任务栏上的对话框除外)是应用程序,一旦模态对话框关闭,就不能再关闭了。确切地说,主消息循环接收PretendModal(WTL :: CMessageLoop :: Run()中的WM_QUIT给出了它,但它仍然在这个特技之后挂起(主框架窗口关闭,WM_QUIT如果我在ATLTRACE2内使用单独的CMessageLoop(而不是“全局”),则整个过程表现相同。

甚至将另一个单独的PretendModal新实例移动到其自己的线程中(在所有消息循环都是线程本地之后)似乎无法解决此问题。这让我感到困惑的是我在这里做错了什么。

注意:CMessageLoopIDCANCEL的处理程序从消息循环中删除对话框类(即消息过滤器)。

问题

在尝试使用无模式对话框模拟模式对话框时,我做错了什么?或者,如何在使用仅派生的模态对话框时捕获 Ctrl + C (和 Ctrl + Ins )来自IDOK

班级

CDialogImpl<T>

1 个答案:

答案 0 :(得分:1)

所以同时我设法实现了我想要的。这似乎工作得很好,我还没有发现任何负面影响。

为了做我想做的事,我引入了一个EmulateModal()函数,它模仿DoModal()的{​​{1}}函数。

该功能如下:

DialogImpl

void EmulateModal(_In_ HWND hWndParent = ::GetActiveWindow(), _In_ LPARAM dwInitParam = NULL) { ATLASSERT(!m_bModal); ::EnableWindow(hWndParent, FALSE); Create(hWndParent, dwInitParam); ShowWindow(SW_SHOW); m_loop.AddMessageFilter(this); m_loop.Run(); ::EnableWindow(hWndParent, TRUE); ::SetForegroundWindow(hWndParent); DestroyWindow(); } 成员是[{1}}派生类所拥有的m_loop(也会继承CMessageLoop,如问题所示)。

唯一需要的其他特殊处理是将以下代码添加到命令ID处理程序中,该处理程序监视CDialogImplCMessageFilter(在我的情况下,这两者都是关闭对话框) ,即在IDOK内。

IDCANCEL

将消息过滤器(即OnCloseCmd)从消息循环之前移至调用if(m_bModal) { EndDialog(wID); } else { m_loop.RemoveMessageFilter(this); PostMessage(WM_QUIT); } 非常重要。退出“内部”消息循环也是非常重要,该消息循环由PreTranslateMessage派生类所拥有,其DestroyWindow()CDialogImpl调用

所以要点是:

  1. 从我的问题中删除Run()方法
  2. 使用“内部”消息循环而不是使用顶级消息循环