加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

visual-c – 如何在WTL中模拟模态对话框?

发布时间:2020-12-16 07:17:51 所属栏目:百科 来源:网络整理
导读:模态对话框很好用,也很容易使用.问题是他们不允许我自己处理消息循环.所以我想我也许可以使用无模式对话框来模拟模态,并且仍然自己负责消息循环以便处理加速器. 目标 我想要实现的是在对话框具有焦点时按Ctrl C(和Ctrl Ins)的能力,然后我希望能够通过将一些
模态对话框很好用,也很容易使用.问题是他们不允许我自己处理消息循环.所以我想我也许可以使用无模式对话框来模拟模态,并且仍然自己负责消息循环以便处理加速器.

目标

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

我现在正在做什么

现在我现在所做的是从CDialogImpl< T>中导出我的对话框类.和CMessageFilter为了让我负责PreTranslateMessage.在那里,我只使用CAccelerator :: TranslateAccelerator和CWindow :: IsDialogMessage来处理加速器和对话框消息.

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

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

然后我通过名称PretendModal为DoModal创建了一个代理,它使用“全局”消息循环.

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

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

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

在尝试使用无模式对话框模拟模态对话框时,我做错了什么?或者,当使用仅从CDialogImpl< T>派生的模态对话框时,如何捕获Ctrl C(和Ctrl Ins).

班级

class CAboutDlg :
    public CDialogImpl<CAboutDlg>,public CMessageFilter
{
    CAccelerator m_accel;
public:
    enum { IDD = IDD_ABOUT };

    BEGIN_MSG_MAP(CAboutDlg)
        MESSAGE_HANDLER(WM_INITDIALOG,OnInitDialog)
        COMMAND_ID_HANDLER(IDOK,OnCloseCmd)
        COMMAND_ID_HANDLER(IDCANCEL,OnCloseCmd)
    END_MSG_MAP()

    virtual BOOL PreTranslateMessage(MSG* pMsg)
    {
        if (!m_accel.IsNull() && m_accel.TranslateAccelerator(m_hWnd,pMsg))
            return TRUE;
        return CWindow::IsDialogMessage(pMsg);
    }

    LRESULT OnInitDialog(UINT,WPARAM,LPARAM,BOOL&)
    {
        m_accel.Attach(AtlLoadAccelerators(IDD));
        if (m_bModal)
        {
            CMessageLoop* pLoop = _Module.GetMessageLoop();
            pLoop->AddMessageFilter(this);
        }
        return TRUE;
    }

    void PretendModal(HWND hwndParent = ::GetActiveWindow())
    {
        CMessageLoop* pLoop = _Module.GetMessageLoop();
        if (pLoop && ::IsWindow(hwndParent))
        {
            HWND dlg = Create(*this);
            if (::IsWindow(dlg))
            {
                ShowWindow(SW_SHOW);
                pLoop->Run();
            }
        }
    }

    LRESULT OnCloseCmd(WORD,WORD,HWND,BOOL&)
    {
        if (m_bModal)
            EndDialog(0);
        else
        {
            CMessageLoop* pLoop = _Module.GetMessageLoop();
            pLoop->RemoveMessageFilter(this);
            ::DestroyWindow(*this);
        }
        return 0;
    }
};

解决方法

所以同时我设法实现了我想要的.这似乎很好地工作,我还没有发现任何负面的副作用.

为了做我想做的事,我介绍了一个EmulateModal()函数,它模仿DialogImpl的DoModal()函数.

该功能如下:

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();
}

m_loop成员是CDialogImpl派生类所拥有的CMessageLoop(它也继承自CMessageFilter,如问题所示).

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

if(m_bModal)
{
    EndDialog(wID);
}
else
{
    m_loop.RemoveMessageFilter(this);
    PostMessage(WM_QUIT);
}

在调用DestroyWindow()之前从消息循环中删除消息过滤器(即PreTranslateMessage)很重要.退出由CDialogImpl派生类所拥有的“内部”消息循环以及从上面的EmulateModal()调用其Run()也非常重要.

所以要点是:

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读