1 在多文档程序中给视图指定标题信息
要定制MDI程序中子框架的标题,想来是一个比较简单的事情,按照习惯,SetWindowText就可以设置窗口的标题,然而在实际实现时,却并不是这么回事,首先时调用这个函数的时机,也就是在什么地方调用这个函数,另外就是这个函数真的会起作用么?要弄清楚这个问题,我们首先就要了解MDI程序中子框架的创建过程,在了解了这个创建过程之后,再来考虑解决办法。
1.1 子框架窗口的创建过程
在MDI程序中,文档模板类CDocTemplate类的CreateNewFrame成员函数是子框架窗口开始创建的起点(当然,如果说OpenDocumentFile为起点也不为过,因为这个函数也调用CreateNewFrame来创建子框架创建及视图),因此,就从CreateNewFrame函数为起点,来跟踪子框架的创建过程。
于是,我们看到其执行线索是这样的:
多文档程序子框架及其视图创建过程
1:调用CDocTemplate::CreateNewFrame启动子框架的创建流程
1.1:使用动态创建技术,在内存中分配空间,创建CChildFrame对象【doctempl.cpp260】
1.2:以WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE为缺省风格,调用CMDIChildWnd::LoadFrame从资源创建子框架【doctempl.cpp273】
1.2.1 调用CFrameWnd::GetIconWndClass注册子框架窗体类【winmdi.cpp619】
1.2.1.1 调用CChildFrame::PreCreateWindow【winfrm.cpp662】
1.2.1.2 调用AfxRegisterWndClass注册窗体类【winfrm.cpp672】
1.2.2 调用CMDIChildWnd::Create函数创建子框架窗体【winmdi.cpp619行】
1.2.2.1 调用CChildFrame::PreCreateWindow【winmdi.cpp528】
1.2.2.2 构造MDICREATESTRUCT结构【winmdi.cpp537】
1.2.2.3 给MDI的客户窗口发送WM_MDICREATE消息创建子框架来创建边框窗口【winmdi.cpp550】
其中又发送了WM_NCCREATE和WM_CREATE消息给子框架对象,这样就导致对应的CMDIChildWnd::OnNcCreate函数、CChildFrame::OnCreate 函数 被调用。
1.2.2.3.1 CMDIChildWnd::OnNcCreate【winmdi.cpp1026】
1.2.2.3.1.1 调用CFrameWnd::OnNcCreate,汇编代码
1.2.2.3.1.2 调用PreCreateWindow
1.2.2.3.1.3 调用SetWindowLong,设置窗体风格
1.2.2.3.2 CChildFrame::OnCreate (如果在CChildFrame中重载)
1.2.2.3.2.1 调用CMDIChildWnd::OnCreate函数
1.2.2.3.2.1.1 调用OnCreateHelper【winmdi.cpp1046】
1.2.2.3.2.1.1.1调用CWnd::OnCreate【winfrm.cpp632】汇编代码
1.2.2.3.2.1.1.2调用:OnCreateClient【winfrm.cpp636】
1.2.2.3.2.1.1.2.1 调用CreateView【winfrm.cpp618】创建视图对象
1.3 至此已经创建了子框架对象,视图对象;CreateNewFrame函数返回;
2 调用CDocTemplate::InitialUpdateFrame初始化边框,该函数在【doctempl.cpp323】
2.1调用CFrameWnd::InitialUpdateFrame【winfrm.cpp732】
2.1.1将第一个视设置为活动视【winfrm.cpp742】
2.1.2给所有相关视图及控件发送WM_INITIALUPDATE消息【winfrm.cpp749】,这样导致视图的OnInitialUpdate函数被调用
2.1.2.1 CXXXView::OnInitialUpdate:(该函数可以被重载的)
2.1.2.1.1调用CView::OnUpdate函数
2.1.2.1.1.1调用CView::Invalidate(汇编代码)
2.1.3调用视图对象的OnActivateFrame以提供保存焦点的机会【winfrm.cpp753】
2.1.4调用CMDIChildWnd::ActivateFrame激活边框【winfrm.cpp764】
2.1.5调用CMDIChildWnd::OnUpdateFrameTitle【winfrm.cpp764】
2.1.5.1调用:CMDIFrameWnd::OnUpdateFrameTitle设置父窗体的标题
2.1.5.1.1如果具有FWS_ADDTOTITLE风格,则设置标题
2.1.5.2如果具有FWS_ADDTOTITLE风格,则设置子框架的标题【winmdi.cpp922】
2.2 CDocTemplate::InitialUpdateFrame执行完毕
1.2 修改标题的方法
方法1:去掉 FWS_ADDTOTITLE的窗体风格
从上述的“子框架窗口的创建过程”可以发现,和子框架的标题设置有关的是CDocTemplate::InitialUpdateFrame部分,在该函数执行的最后一步,会调用OnUpdateFrameTitle函数来设置子框架的标题信息。由于这个函数是窗体显示前的最后一步,因此,要是我们只是简单的直接在视图类中处理WM_INITIALUPDATE消息,即OnInitialUpdate中来调用SetWindowsText来设置子框架的标题的话,所设置的信息又会被OnUpdateFrameTitle所设置的标题覆盖。
不过,从OnUpdateFrameTitle的实现代码可以发现,在该函数中,进行了是否设置了FWS_ADDTOTITLE风格的判断,如果具有该风格,就给窗口设置一个默认的标题信息。在默认的情况下,CDocTemplate的CreateNewFrame函数以WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE为缺省风格来调用LoadFrame函数创建子框架,这样窗口的风格中就包含了FWS_ADDTOTITLE。不过,从上面的创建过程,也可以发现,在真正开始创建子框架之前,MFC会调用PreCreateWindow这个可被重载的函数,这样就给我们提供了一个机会来修改子框架的创建风格,于是,我们就找到了一个修改标题的方法,这个方法其实很简单,实施步骤如下:
第一步:重载CMDIChildFrame的PreCreateWindow函数,去掉FWS_ADDTOTITLE:
cs.style &= ~FWS_ADDTOTITLE;
第二步:处理视图的WM_INITIALUPDATE消息,在其中调用SetWindowText来把标题设置成想要的内容
方法2:重载OnUpdateFrameTitle函数
该函数是CMDIFrameWnd类的一个虚函数,在以前版本的MFC中,这个函数没有暴露出来,不过到了4.2之后,这个函数就已经可以方便的通过ClassWizard来进行重载了,因此,修改标题的最简单的方法就是重载此函数,在此函数中设置标题的内容。
不过,在重载此方法的时候,可以不调用基类的方法,如果要调用,则自己设置标题信息的代码一定要放置在基类方法的调用之后,自己设置的标题才不会被基类方法设置点标题覆盖。