一、代码实现
1、从View类派生3个视图类
2、 CMainFrame中添加 CSplitterWnd m_right; CSplitterWnd m_left;//要分割的窗口
3、代码实现如下:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
m_left.CreateStatic(this,1,2);//创建类名为MDI42窗口
m_left.CreateView(0,0,RUNTIME_CLASS(CLeftView),CSize(180,550),pContext);//创建子窗口左边视图view42
//CreateView里面会关联到当前文档当中去
m_right.CreateStatic(&m_left,2,1,WS_CHILD | WS_VISIBLE,m_left.IdFromRowCol(0,1));//创建子窗口右边MDI42
m_right.CreateView(0,0,RUNTIME_CLASS(CRightView),CSize(100,350),pContext);//创建右MDI42子窗口上面视图view42
m_right.CreateView(1,0,RUNTIME_CLASS(CBottomView),CSize(100,300),pContext);//创建右MDI42子窗口下视图view42
return TRUE;
// return CFrameWnd::OnCreateClient(lpcs, pContext);//屏蔽原来的CView类,如果不屏蔽,则会创建ID号一样为AFX_IDW_PANE_FIRST的窗口,说明同层子窗口id是可以相同的
}
4、窗口关系分析(父窗口、子窗口)
第一级: CMainFrame
第二级:CSplitterWnd m_left (AfxMDIFrame42d) 、状态栏statusbar32、4个AfxControlBar42d
第三级(m_left):CSplitterWnd m_right (AfxMDIFrame42d)、AfxFrameOrView42d(C**View类)
第四级(m_right):AfxFrameOrView42d(C**View类)、AfxFrameOrView42d(C**View类)
二、原理分析
1、窗口类派生关系
CSplitterWnd:CWnd、C**View : CView
2、创建分析
CreateView时,有一个pContext->m_pCurrentDoc,View在创建之后,会将当前View类对象成员遍历m_pDocument赋值m_pCurrentDoc,同时将该View对象指针加入到m_pCurrentDoc中视图链表中。
Q1、既然加入了视图链表中,如何实现CFrameWnd::m_ActiveView为活动视图呢?
鼠标点击各个视图窗口,会发送一个WM_MOUSEACTIVATE消息给CView::OnMouseActivate消息,然后它会将view的this指针保存到CMainFrame的 m_ActiveView中。WM_LBUTTONDOWN稍后发送。
CFrameWnd* pParentFrame = GetParentFrame();
{
CWnd* pParentWnd = GetParent(); // start with one parent up
while (pParentWnd != NULL)
{
if (pParentWnd->IsFrameWnd())// CSpliterWnd派生于CWnd,而CMainFrame派生于CFrameWnd,对于上面m_left中的CBottomView而言,首先获得的是CSplitterWnd,但是它不是派生于CFrameWnd,继续往上找框架类。
//CWnd::IsFrameWnd() return FALSE,CFrameWnd::IsFrameWnd()return TRUE
return (CFrameWnd*)pParentWnd;
pParentWnd = pParentWnd->GetParent();
}
}
pParentFrame->GetActiveView();//获取当前框架的m_ActiveView类
//然后再验证是不是CView是不是m_ActiveView,是就再激活一次
pParentFrame->SetActiveView(this);//不是,就保存到m_ActiveView中
CSplitterWnd::GetActivePane()也是这样的方式,基本一样。
可以在CView::OnMouseActivate()下断点,在调试运行。
Q2、如果C**View派生于CEditView类,Edit控件是何时创建?
当new 一个CEditView时,构造函数CEditView::CEditView() : CCtrlView(_T("EDIT"), NULL),这样就可在CCtrlView中创建Edit控件,调用pView-CreateEx("Edit"),就将CEditView关联了Edit控件窗口。
3、位置分析
创建窗口之后,一般不会去设置它的位置,现在创建一个窗口,OnCreate消息内创建其子窗口。
一旦窗口位置改变,系统发送WM_SIZE消息,每个窗口,接受到OnSize(),里面一般会设置它的子窗口的位置。设置之后,子窗口收到WM_SIZE消息,由它来设置自身的位置。CView不处理OnSize消息。
4、MFC框架图
类图显示了MFC框架结构,不同的文档模板有不用的菜单资源。CFramWnd中有一个m_pAcitveView,可获得当前活动视图,通过它可以获得当前活动文档m_pActiveDocment。
当文件点击保存时,CMainFrame收到OnFileSave消息,然后pActiveView、pDocument、CMainFrame、CWinApp顺序处理,如果要保存多个文档,则重载OnSaveDocument(LPCTSTR lpszPathName),写入自己的保存数据的函数。
参考:1、如何在MFC单文档中创建多视图
2、建立单文档多视图
3、MFC切割窗口
4、单文档实现多视图
5、VC++ 6.0使用CSplitterWnd类分割窗体,拆分窗体改变分隔条的宽度
7、用Win32实现类似MFC中Draw3dRect函数的功能