MFC技术系列(四)--Frame窗口(2)

本文深入探讨了MFC中的MDI(多重文档界面)框架,包括CMDIChildWnd和CMDIFrameWnd类的作用及其实现原理。文章详细介绍了如何通过MDI创建和管理多个子窗口,以及这些操作涉及的关键步骤和技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接上篇, "Frame窗口(1)" 

3. MDI

这是MFC提供的另一个复杂的Doc-View框架。它由CDocument, CView的派生类和CMDIChildWndwinmdi.cpp)的派生类(Frame Window)构成一类Frame窗口,这些类的RUNTIMECLASS宏信息由CMultiDocTemplate类(docmulti.cpp)管理,并同样注册到CDocManager类。同SDI一样,这些运行时类信息也是由前面提到的一对宏来管理的。

MFC还提供了一个容纳这些子Frame窗口的父Frame窗口基类:CMDIFrameWnd类(winmdi.cpp)。对于MDI应用,其Top-level窗口即为该类的派生类。

MDI创建一个View的过程如下:

1.       如果是点击New菜单项(刚启动时默认打开该菜单FileNew),那么从App类进入到CDocManagerOnFileNew方法。该方法中,如果注册的Document类型不只一个,那么会弹出CNewTypeDlg,选择一种Document。然后进入到下一步。如果要改写该默认行为,那么可以自己编写OnFileNew的响应逻辑,而不是调用基类CWinApp的响应方法。

2.       既然是MDI,那么执行CMultiDocTemplateOpenDocumentFile虚方法。同SDI一样,该方法也是创建过程的核心方法。其实,既然从一个DocTemplate来看,这里面的基本过程基本同SDI。即:先调用CreateNewDocument创建Document,然后调用CreateNewFrame创建子Frame窗口,然后,如果是创建一个空文档,那么调用Document类的OnNewDocument虚方法,如果是打开一个已有文档,那么调用OnOpenDocument虚方法。在CreateNewFrame过程中,将调用CreateView方法创建对应的View窗口

3.       CreateNewFrame方法中,将调用LoadFrame装载子Frame窗口。此时,有两个重要的成员,即:m_hMenuSharedm_hAccelTable,分别为子Frame窗口的菜单和加速键表。它们从对应的MultiDocTemplate实例中获得(doc template构造的第一个参数即为Frame资源ID)。注意,在MFC框架中,为能够减少代码,将Frame窗口的Title图标、菜单和加速键的资源ID都规定为同一个值(既然这些资源的类型(RT值)不同,所以不用担心会被互相覆盖)

4.       最后,调用InitUpdateFrame。具体描述参见SDI部分

 

MFC的上述两个MDI窗口类都是对WM_MDI系列消息的封装。如下几点需要特别注意:

1.       CMDIChildWnd的父窗口为一个被称为MDI client的窗口。当你将所有子Frame窗口关闭后,位于空白区域的就是该窗口。该窗口的父窗口才是CMDIFrameWnd窗口

2.       关于子Frame窗口的消息,都是发送到这个MDI client窗口。它其实是child frame窗口和main frame窗口之间的纽带。可以直接通过main frame窗口的m_hWndMDIClient公有成员获得该窗口的句柄

3.       基于DocTemplate,通过注册不同的文档模板,我们很容易创建出相同document的不同view,它们实际由不同的child frame窗口容纳。如果要在自己的代码中创建一个child frame窗口,那么完全可以参考上面过程的主要方法。

 

MDI各个主要场景下,消息序列:

1.       创建一个child frame

<00001> 000301DC 
    
    
     
     S WM_MDICREATE
    
     lpmdic:
    
    
     
     0012F
    
    
    
    
     
     18C
    
    
    
    
<00002> 000301DC R WM_MDICREATE hwndChild:
    
    
     
     000F
    
    0546
    
    
<00003> 000301DC 
    
    
     
     S WM_MDISETMENU
    
     hmenuFrame:000504E7 hmenuWindow:000804B5
    
    
<00004> 000301DC R WM_MDISETMENU hmenuFrameOld:000704DF
    
    
<00005> 000301DC 
    
    
     
     S WM_MDIREFRESHMENU
    
    
    
    
<00006> 000301DC R WM_MDIREFRESHMENU hmenuFrame:000504E7
    
    

2.       关闭一个child frame

<00001> 000301DC 
    
    
     
     S WM_MDIDESTROY
    
     hwndChild:
    
    
     
     000F
    
    0546
    
    
<00002> 000301DC 
    
    
     
     S WM_MDISETMENU
    
     hmenuFrame:000704DF hmenuWindow:00000000
    
    
<00003> 000301DC R WM_MDISETMENU hmenuFrameOld:000504E7
    
    
<00004> 000301DC 
    
    
     
     S WM_MDIREFRESHMENU
    
    
    
    
<00005> 000301DC R WM_MDIREFRESHMENU hmenuFrame:000704DF
    
    
<00006> 000301DC R WM_MDIDESTROY
    
    

3.       frame窗口的排列

层叠:WM_MDICASCADE

平铺:WM_MDITILE

排列图标:WM_MDIICONARRANGE

4.       从菜单上选择背后的某个子frame窗口,此时,当前子Frame窗口将被切换到另一个子Frame窗口(从spy结果来看,如果是直接使用鼠标选中某个子Frame,则不会产生WM_MDIACTIVATE消息)

<00001> 000301DC 
    
    
     
     S WM_MDIACTIVATE
    
     hwndChildActivate:0036067E
    
    
<00002> 000301DC 
    
    
     
     S WM_MDIREFRESHMENU
    
    
    
    
<00003> 000301DC R WM_MDIREFRESHMENU hmenuFrame:000504E7
    
    
<00004> 000301DC 
    
    
     
     S WM_MDISETMENU
    
     hmenuFrame:000504E7 hmenuWindow:000804B5
    
    
<00005> 000301DC R WM_MDISETMENU hmenuFrameOld:000504E7
    
    
<00006> 000301DC R WM_MDIACTIVATE
    
    

5.       在子Frame窗口的系统菜单中选择“下一个”

<00001> 000301DC 
    
    
     
     S WM_MDINEXT
    
     hwndChild:0036067E fNext:False
    
    
<00002> 000301DC 
    
    
     
     S WM_MDIREFRESHMENU
    
    
    
    
<00003> 000301DC R WM_MDIREFRESHMENU hmenuFrame:000504E7
    
    
<00004> 000301DC 
    
    
     
     S WM_MDISETMENU
    
     hmenuFrame:000504E7 hmenuWindow:000804B5
    
    
<00005> 000301DC R WM_MDISETMENU hmenuFrameOld:000504E7
    
    
<00006> 000301DC R WM_MDINEXT
    
    

6.       frame窗口被最小化(??)

7.       frame窗口被最大化(WM_MDIMAXIMIZE)(不过使用spy++未捕获到该消息

8.       frame窗口从最小化,或者最大化状态恢复(WM_MDIRESTORE)(不过使用spy++未捕获到该消息

 

可以看出,几乎每一个涉及到菜单状态的行为发生过程中,基本都是菜单的切换。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值