MFC技术系列(三)--对话框(1)

对话框(Dialog)是应用中常用的界面形态。一个典型的对话框的风格如下:

普通风格:WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SYSMENU

扩展风格:WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT | WS_EX_APPWINDOW

Dialog从消息处理的角度又可以分为模态和非模态对话框。其中,模态对话框实际是调用了CWndRunModalLoop方法(代码位置:%Program Files%/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/dlgcore.cpp)。该方法的核心为一个消息处理循环,这个过程同CWinThread所封装的消息循环类似,使用了PeekMessage非阻塞地获取消息,可分为两个阶段:

第一阶段:空闲状态处理。此时,消息队列中没有任何消息,此时无法退出模态循环。应用可以通过一些标志(Flag)来控制是否产生空闲消息,这些标志如下:

1.        MLF_SHOWONIDLE:在空闲时,如果窗口不可见则让其可见

2.        MLF_NOIDLEMSG:不产生WM_ENTERIDLE消息

3.         MLF_NOKICKIDLE:不产生WM_KICKIDLE消息

注:空闲消息主要指WM_ENTERIDLEWM_KICKIDLE

第二阶段:非空闲消息处理。此时,可以通过调用EndModalLoop来结束模态循环,但该方法并非直接结束循环,而只是设置一个标志(WF_CONTINUEMODAL),等到消息队列处理完当前消息后,模态循环判断该标志决定是否退出。消息队列循环的主要由AfxPumpMessage方法封装,它封装了Windows上典型的消息循环,但是在Translate Message前增加了下面的判断:

pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)

其中,pState为线程数据结构,存储UI线程的一些数据,尤其是消息相关的数据。前一个条件表示不处理WM_KICKIDLE消息,后一个条件提供给了应用程序一个机会,便于对消息的过滤(CWndPreTranslateMessage虚方法)。

 

既然模态对话框调用了CWnd的方法,因此,其它类型的窗口也可以调用RunModalLoop让自己进入模态,即:模态并非对话框的专利。

 

CWinThread的消息循环:

代码位置:CWinThreadRun方法。%Program Files%/Microsoft Visual Studio .NET 2003/Vc7/atlmfc/src/mfc/thrdcore.cpp

 

1.1           模态和非模态对话框

       CWnd类中,提供了BeginModalStateEndModalState两个方法,实际上就是分别让该窗口EnableDisable(使用EnableWindow API方法)。对于CDialog,调用DoModal使其呈现模态。本质上,该对话框仍然是非模态(普通)窗体,只是其父窗口被Disable了。DoModal做了如下事情:

1.       依据resource template创建对话框。由于不是使用CreateWindowEx,所以这之前也需要hook消息,以便消息能进入到该窗口的WndProc方法中。

2.       使父窗口Disable

3.       调用基类的RunModalLoop,进入模态循环

4.       调用EndModalLoop,设置退出标志,然后发送一个WM_NULL消息到队列中,退出模态循环

5.       恢复父窗口状态(Enable,且SetActive

6.       销毁窗口

7.       Unhook消息

8.       Unlockfree前面的资源

 

对于非模态,就是普通的窗口形态,即:通常使用ShowWindow即可,复杂一些可以使用SetWindowPos方法。

如果有如下的需求,该如何去做呢?有一个模态和一个非模态对话框,两者的父窗口均为主Frame窗口。当模态对话框活动时,需要可以操作非模态窗口。

当模态窗口显示时,主Frame窗口已经被Disable。此时,作为其子窗口的非模态窗口也随之被Disable,也就是说也无法操作它。其实,既然它被Disable了,就只需要调用EnableWindow将其Enable就可以了。

前面已经提到了模态窗口的消息循环。此时,它和CWinThread所封装的消息循环共用同一个消息队列(既然都在一个线程中),且在核心消息的获取和分发上,两者是相同的(AfxPumpMessage方法),不同的主要只是多了对模态的管理。所以,如果此时从该模态窗口发送一个消息到父窗口,那么父窗口是可以接收到消息的,而不会因为窗口被Disable了,就无法接收消息了。如果一个窗口调用EnableWindowDisable了,只是意味着它无法接收用户的键盘和鼠标消息,其它消息仍然可以接收到。注意,这里说的是用户触发的消息,也就是用户的行为导致系统触发的消息,此时,父窗口无法接收到。如果应用自己发送一个这类消息,那么同其它消息一样,是可以被接收到的。

 

(未完,待续......)

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值