MFC 模态对话框的实现原理

news2024/11/15 15:47:18

参考自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 的消息循环就被阻断。整个程序的消息循环有模态对话框中得消息循环取代。所以给父窗口发送的非窗口消息,一样可以响应。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1717526.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

The book

Deep Learning for Coders with Fastai and PyTorch: AI Applications Without a PhD is the book that forms the basis for this course. We recommend reading the book as you complete the course. There’s a few ways to read the book – you can buy it as a paper bo…

到无穷大和更远,用分形更好

文章目录 一、说明二、分形到底是什么?三、更多更深刻的四、引进无穷小会产生什么样的怪事?五、希尔伯特曲线六、还有什么有趣的要补充的吗? 一、说明 ​​​​​​​数学领域有太多有趣的领域,领域我特别感兴趣。这是一个奇妙的…

【PostgreSQL17新特性之-冗余IS [NOT] NULL限定符的处理优化】

在执行一个带有IS NOT NULL或者NOT NULL的SQL的时候,通常会对表的每一行,都会进行检查以确保列为空/不为空,这是符合常理的。 但是如果本身这个列上有非空(NOT NULL)约束,再次检查就会浪费资源。甚至有时候…

经验分享:如何搭建一个有效的知识库管理系统

打开知乎,发现很多朋友在问如何搭建一个有效的知识库管理系统,所以今天LookLook同学就来跟大家分享一下我是怎么搭建一个既实用又高效的知识库管理系统的。 一、明确需求,定位清晰 首先,你得想清楚你要搭建的知识库管理系统是用来…

时钟、复位与上电初始化

目录 1. 时钟2. 复位2.1. 异步复位 同步释放2.2. Xilinx FPGA复位设计基于PLL锁定(locked)复位设计 3. 上电初始化 1. 时钟 2. 复位 FPGA中复位设计总结 深入理解复位—同步复位,异步复位,异步复位同步释放(含多时钟域&#xff0…

element table表格行列合并span-method,根据数据动态行列合并

表格行列合并需要用到 table的方法 span-method 根据数据来进行动态的行列合并&#xff0c;实例如下&#xff1a; <el-table:data"tableData":span-method"objectSpanMethod" style"width: 100%"><el-table-columnprop"key"l…

【python】OpenCV—Color Detection

学习来自 如何使用 OpenCV Python 检测颜色 import cv2 import numpy as npdef red_hsv(img, saveFalse):lower_hsv1 np.array([0, 175, 20])higher_hsv1 np.array([10, 255, 255])lower_hsv2 np.array([170, 175, 20])higer_hsv2 np.array([10, 255, 255])mask1 cv2.inR…

基于STM32的轻量级Web服务器设计

文章目录 一、前言1.1 开发背景1.2 实现的功能1.3 硬件模块组成1.4 ENC28J60网卡介绍1.5 UIP协议栈【1】目标与特点【2】核心组件【3】应用与优势 1.6 添加UIP协议栈实现创建WEB服务器步骤1.7 ENC28J60添加UIP协议栈实现创建WEB客户端1.8 ENC28J60移植UIP协议并编写服务器测试示…

[代码复现]Self-Attentive Sequential Recommendation(ing)

参考代码&#xff1a;SASRec.pytorch 可参考资料&#xff1a;SASRec代码解析 前言&#xff1a;文中有疑问的地方用?表示了。可以通过ctrlF搜索’?。 环境 conda create -n SASRec python3.9 pip install torch torchvision因为我是mac运行的&#xff0c;所以device是mps 下面…

npm install pubsub-js报错的解决汇总

我在练习谷粒商城P83时&#xff0c;选择分类时触发向后端请求选择分类catId绑定的品牌数据&#xff0c;发现前端控制台报错&#xff1a; "PubSub is not definded",找不到pubsub。 因为缺少pubsub包&#xff0c;所以开始安装此包。 于是在网上一顿搜索猛如虎&…

C# :IQueryable IEnumerable

1. IEnumerable namespace System.Collections: public interface IEnumerable {public IEnumerator GetEnumerator (); }public interface IEnumerator {pubilc object Current { get; }public bool MoveNext ();public void Reset (); }IEnumerable 只有一个方法 GetEnumera…

django使用fetch上传文件

在上一篇文章中&#xff0c;我包装了fetch方法&#xff0c;使其携带cookie。但是之前fetch传递的是json数据&#xff0c;现在有了一个上传文件的需求&#xff0c;因此需要进行修改&#xff1a; const sendRequest (url, method, data) > {const csrftoken Cookies.get(cs…

C++ | Leetcode C++题解之第119题杨辉三角II

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> getRow(int rowIndex) {vector<int> row(rowIndex 1);row[0] 1;for (int i 1; i < rowIndex; i) {row[i] 1LL * row[i - 1] * (rowIndex - i 1) / i;}return row;} };

HTML动态响应2-Servlet+Ajax实现HTTP前后台交互方式

作者:私语茶馆 前言 其他涉及到的参考章节: HTML动态响应1—Ajax动态处理服务端响应-CSDN博客 Web应用JSON解析—FastJson1.2.83/Tomcat/IDEA解析案例-CSDN博客 HTML拆分与共享方式——多HTML组合技术-CSDN博客 1.场景: WEb项目经常需要前后端交互数据,并动态修改HTML页…

洛谷 P1194 买礼物

题目来源于&#xff1a;洛谷 题目本质&#xff1a;图论生成树 代码如下&#xff1a; #include<bits/stdc.h> using namespace std; const int N10005; const int M250005; int n,m; int cnt,flag,px,py; int ans0; //ans表示最小花费的钱数 int f[N]; struct Edge{in…

list~模拟实现

目录 list的介绍及使用 list的底层结构 节点类的实现 list的实现 构造函数 拷贝构造 方法一&#xff1a;方法二&#xff1a; 析构函数 赋值重载 insert / erase push_/pop_(尾插/尾删/头插/头删) begin和end&#xff08;在已建立迭代器的基础上&#xff09; 迭代…

Windows【工具 06】mklink创建符号链接和硬链接(实现文件夹不同磁盘存储)

mklink创建符号链接和硬链接 1.创建符号链接1.1 目录符号链接1.2 文件符号链接 2.创建硬链接3.区别3.1 符号链接&#xff08;Symbolic Link&#xff09;3.2 硬链接&#xff08;Hard Link&#xff09; mklink是Windows中用于创建符号链接&#xff08;symbolic links&#xff09;…

数据库管理哪家强?Devart VS Navicat 360°全方位对比解析

今天我们向大家推荐的是两个开发环节的主流数据库管理品牌&#xff0c;那么你知道这两款数据库管理软件品牌与 数据库引擎配套的管理软件有什么区别吗&#xff1f;小编这就360全方位为您解答&#xff1a; ★ 品牌介绍 Devart&#xff1a;拥有超过20年的经验&#xff0c;利用最…

docker目录挂载失败:Check if the specified host path exists and is the expected type

docker目录挂载失败&#xff1a;Check if the specified host path exists and is the expected type docker目录挂载命令&#xff0c;其目的是为了达到修改linux上的文件同步到容器上&#xff0c;从而实现修改容器的配置文件。 在docker目录挂载或启动容器时报错&#xff0c…

若依前后端分离Spring Security新增手机号登录

备忘贴 转自&#xff1a;【若依RuoYi短信验证码登录】汇总_数据库_z_xiao_qiang-RuoYi 若依 配置Security: 按照Security的流程图可知&#xff0c;实现多种方式登录&#xff0c;只需要重写三个主要的组件&#xff0c;第一个用户认证处理过滤器&#xff0c;第二个用户认证tok…