美文网首页
MFC 模态对话框的实现原理

MFC 模态对话框的实现原理

作者: 风轻云淡宇 | 来源:发表于2024-05-29 11:15 被阅读0次

参考自MFC 模态对话框的实现原理 - 西昆仑 - OSCHINA - 中文开源技术交流社区

1. 模态对话框

在涉及 GUI 程序开发的过程中,常常有模态对话框以及非模态对话框的概念

模态对话框:在子界面活动期间,父窗口是无法进行消息响应,独占用户输入;
非模态对话框:各窗口之间不影响

主要区别:非模态对话框与 APP 共用消息循环,不会独占用户。
模态对话框独占用户输入,其他界面无法响应

在用户层的主要逻辑如下:

TestDlg dlg;

if (dlg.DoModal() == IDOK)
{
           //处理完毕后的操作
}
.......//后续处理

在具体实现中,有如下几个步骤:
1. 让父窗口失效 EnableWindow (parentWindow, FALSE)
2. 建立模态对话框自己的消息循环(RunModalLoop)
3. 直至接收关闭消息,消息循环终止,并销毁窗口。

INT_PTR CDialog::DoModal()
{
    //对话框资源加载
    ......

    //在创建模态窗口之前先让父窗口失效,不响应键盘、鼠标产生的消息
    HWND hWndParent = PreModal();
    AfxUnhookWindowCreate();
    BOOL bEnableParent = FALSE;

    if (hWndParent && hWndParent != ::GetDesktopWindow() && ::IsWindowEnabled(hWndParent))
    {
        ::EnableWindow(hWndParent, FALSE);
        bEnableParent = TRUE;
                .......
    }

    //创建模态窗口,并进行消息循环,若窗口不关闭,则循环不退出
    AfxHookWindowCreate(this);
    VERIFY(RunModalLoop(dwFlags) == m_nModalResult);

    //窗口关闭,销毁窗口
    DestroyWindow();
    PostModal();

    //释放资源,并让父窗口有效
        pMainWnd->EnableWindow(TRUE);

        //返回
    return m_nModalResult;
}

2. 模态窗口中的消息循环

int CWnd::RunModalLoop(DWORD dwFlags)
{
    //要检查窗口状态是否是模态窗口
    //若状态一直为模态,则一直进行消息循环
    for (;;)
    {
        ASSERT(ContinueModal());

        // phase1: check to see if we can do idle work
        while (bIdle &&
            !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
        {
            ASSERT(ContinueModal());

            // show the dialog when the message queue goes idle
            if (bShowIdle)
            {
                ShowWindow(SW_SHOWNORMAL);
                UpdateWindow();
                bShowIdle = FALSE;
            }

            // call OnIdle while in bIdle state
            if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent != NULL && lIdleCount == 0)
            {
                // send WM_ENTERIDLE to the parent
                ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX, (LPARAM)m_hWnd);
            }
            if ((dwFlags & MLF_NOKICKIDLE) ||
                !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
            {
                // stop idle processing next time
                bIdle = FALSE;
            }
        }

        //在有消息的情况下取消息处理
        do
        {
            ASSERT(ContinueModal());

            // pump message, but quit on WM_QUIT
            if (!AfxPumpMessage())
            {
                AfxPostQuitMessage(0);
                return -1;
            }

            // show the window when certain special messages rec'd
            if (bShowIdle &&
                (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
            {
                ShowWindow(SW_SHOWNORMAL);
                UpdateWindow();
                bShowIdle = FALSE;
            }

            if (!ContinueModal())
                goto ExitModal;

            // reset "no idle" state after pumping "normal" message
            if (AfxIsIdleMessage(pMsg))
            {
                bIdle = TRUE;
                lIdleCount = 0;
            }

        } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
    }

ExitModal:
    m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
    return m_nModalResult;
}

GetMessage 与 PeekMessage 的区别:
GetMessage: 用于从消息队列读取消息。若队列中没有消息,GetMessage 将导致线程阻塞。
PeekMessage: 检测队列中是否有消息,并立即返回,不会导致阻塞。

3. APP 中的消息循环

//thrdcore.cpp   
// main running routine until thread exits   
int CWinThread::Run()  
{  
   // for tracking the idle time state   
   BOOL bIdle = TRUE;  
   LONG lIdleCount = 0;  

   //消息读取乃至分发 当为WM_QUIT时,退出循环 
   for (;;)  
   {  
      //检查是否为空闲时刻
      while (bIdle &&  
            !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))  
      {  
         // call OnIdle while in bIdle state   
         if (!OnIdle(lIdleCount++))  
            bIdle = FALSE; // assume "no idle" state   
      }  

     //有消息,读消息并分发 
     do  
     {  
        // pump message, but quit on WM_QUIT   
        if (!PumpMessage())  
           return ExitInstance();  

        // reset "no idle" state after pumping "normal" message   
        if (IsIdleMessage(&m_msgCur))  
        {  
           bIdle = TRUE;  
           lIdleCount = 0;  
        }  

     }   
     while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));  
   }  
}

4. 模态对话框中局部消息循环和 APP 全局消息循环的关系

4.1 APP 消息循环和模态对话框中局部消息循环的关系

根据上图可以看出,在 APP 的消息循环再派发 ONOK 消息后,调用 ModalDlg 的响应函数,pWnd->OnOk (); 在该消息中,
会 进入模态对话框的消息循环,除非将模态对话框关闭,否则 APP 的 DispatchMessage 函数一直出不来。

一旦创建了模态对话框,进行局部消息循环,那么 APP 的消息循环就被阻断。整个程序的消息循环有模态对话框中得消息循环取代。所以给父窗口发送的非窗口消息,一样可以响应。

由于局部消息循环只在对话框中的一个响应函数中,而全局的消息循环也被阻断,局部循环一直运行,如果用户不进行处理并关闭模态对话框,该循环会一直不退出。其他对话框也得不到处理。

相关文章

  • VC++(七)对话框

    在MFC中,对资源的操作通常都是通过一个与资源相关的类来完成的。 模态对话框domodal 非模态对话框creat...

  • MFC登陆对话框-模态对话框

    基于MFC,用模态对话框创建一个登陆对话框 静态显示图片或对话框 在C*App::InitINstance()中创...

  • 第十三节 MFC的一些概念

    一、模态对话框和非模态对话框Windows对话框分为两类:模态对话框和非模态对话框。模态对话框是这样的对话框,当它...

  • MFC非模态对话框提示等待的实现

    需求:在某个button响应中,可能会执行一段耗时较长的函数,在调用这个函数的过程中,为了提示用户当前程序正在执行...

  • 对话框

    MFC中添加新的对话框 MFC 点击按钮,弹出另一个对话框 MFC新建的对话框如何添加、重写初始化代码

  • QT-dialog

    自定义对话框:  使用QDialog::exec()实现应用程序级别的模态对话框  使用QDialog::op...

  • Qt5 - 5. 对话框 QDialog

    Qt 中使用 QDialog 类实现对话框,通常是设计一个类继承 QDialog。Qt 的对话框分为模态对话框和非...

  • 模态框

    模态对话框 点击这里关闭 /* 定义模态对话框外面的覆盖层样式 */ #modal-overlay {...

  • mfc 非模态对话框 创建、释放 与 传参

    创建 非模态 对话框: 释放对话框内存,仅此即可 传参 构造函数 创建 非模态 对话框时,传参

  • 创建项目

    一、 新建MFC项目 选择:基于对话框 MFC的使用:在共享DLL中使用MFC (程序运行需要dll)在静态库中使...

网友评论

      本文标题:MFC 模态对话框的实现原理

      本文链接:https://www.haomeiwen.com/subject/rvokqjtx.html