一、基本原理
对于多文档App而言,首先是创建CMainFrame窗口和它的子窗口MDIClient。当新建一个文档,则会new CDocument,顺便创建CChildFrame和它的子窗口CView,再次新建时,会建立另一个文档,创建另外一个CChildFrame和它的子窗口CView。对于不同视图,CView的ID标识是AFX_IDW_PANE_FIRST。在本次CChildFrame新建另外的CView时,ID标识要改变,以免重复。
向MDIClient发送WM_MDICREATE创建CChildFrame时,内部会分配子窗口的ID值,另外MDIClient在CManiFrame::OnCreate中调用到CMDIFrameWnd中的一个函数,里面创建时,Menu ID标识也是AFX_IDW_PANE_FIRST,但是MDIClient的创建 CLIENTCREATESTRUCT ccs的里面,可以指定子窗口id标识ccs.id,即子窗口CChildFrame的id标识从这个数字开始编号AFX_IDM_FIRST_MDICHILD。
若要获取激活窗口,则向MDIClient发送WM_GETACTIVATE消息,每个激活的CChildFrame父类CFrameWnd保存一个 CView* m_pViewActive,也就是CChildFrame会有一个主视图。
二、SDI单文档多视图的实现及自由切换【1、2】
主要是获取当前活动文档ActiveDocument,放到context.m_pCurrentDoc,然后再创建的时候,传上下文context信息。
classCMultiViewApp : publicCWinApp{ public: CView* m_pFirstView; CView* m_pOtherView; int m_currentView; CView* m_pView2; CView* m_pView1; } CMultiViewApp::InitInstance() { CDocument* pDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument(); //通过CCreateContext实现第二视图和文档的关联 CCreateContext context; context.m_pCurrentDoc = pDoc;//把视图放在当前文档 UINT m_ID = AFX_IDW_PANE_FIRST + 1;//设置标识作为CMainFrame的子窗口 CRect rect; //为了演示第一种多视图是实现方法,把Vew的实例创建放在了这里 m_pOtherView->Create(NULL, NULL, WS_CHILD, rect, m_pMainWnd, m_ID, &context);} //切换时,设置活动视图,另外就是重新计算,这里要具体看RecalcLayout
……//里面会交换View窗口ID,是因为 CFrameWnd::RecalcLayout will allocate // this "first pane" to that portion of the frame window's client area //not allocated to control bars. m_pOtherView->ShowWindow(SW_HIDE); m_pFirstView->ShowWindow(SW_SHOW); m_pMainWnd->SetActiveView(m_pFirstView); m_pMainWnd->RecalcLayout(); m_pFirstView->Invalidate();
三、CMainFrame中添加不同的CChildFrame【3、4】
1、这个子窗口,只是创建了一个视图,没有指定context.m_pCurrentDoc,在CView::OnCreate()里面会报错。
CMainFrame::OnFuncTwo()
{
//仿照上面的思想,new Frame,点击一个菜单时,切换到这个CChildFrame
m_pFuncOneFrame = new CDemoFrame();
CCreateContext context;
context.m_pNewViewClass = RUNTIME_CLASS(CFuncOneView); //关联的视图类,又它新建一个视图对象
m_pFuncOneFrame->LoadFrame(IDR_MAINFRAME, WS_MAXIMIZEWS_OVERLAPPEDWINDOW, this, &context);
m_pFuncOneFrame->SetWindowText(" 业务逻辑一 ");
m_pFuncOneFrame->ShowWindow(SW_SHOWMAXIMIZED);
m_pFuncOneFrame->InitialUpdateFrame(NULL,true);
}
2、加入文档后
void CMainFrame::OnFuncOne()
{
// TODO: Add your command handler code here
if (m_pFuncOneFrame != NULL) //已创建,则激活它
{
m_pFuncOneFrame->MDIActivate();
return ;
}
m_pFuncOneFrame = new CDemoFrame();
CDemoDoc* m_pDoc = new CDemoDoc(); //new了一个文档
CCreateContext context;
context.m_pNewViewClass = RUNTIME_CLASS(CFuncOneView); //传CView class
context.m_pCurrentDoc = m_pDoc;
m_pFuncOneFrame->LoadFrame(IDR_MAINFRAME, WS_MAXIMIZEWS_OVERLAPPEDWINDOW, this, &context);
m_pFuncOneFrame->SetWindowText(" 业务逻辑一 ");
m_pFuncOneFrame->ShowWindow(SW_SHOWMAXIMIZED);
m_pFuncOneFrame->InitialUpdateFrame(NULL,true);
}
四、总结
CMainFrame建立了MDIClient之后,会new CDocument,然后pFrame
CCreateContext context;
context.m_pCurrentFrame = pOther;//NULL,
context.m_pCurrentDoc = pDoc;//当前文档
context.m_pNewViewClass = m_pViewClass;//CDocTemplate中已经保存有它
context.m_pNewDocTemplate = this;
pFrame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,NULL, &context))
//pParenWnd会在后面设置为MDIClient
//其函数原型
virtual BOOL LoadFrame(UINT nIDResource,DWORD dwDefaultStyle = WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,
CWnd* pParentWnd = NULL,CCreateContext* pContext = NULL);
对于文档管理,三、2中,可将模板放在保存一个位置,或者直接获取pDocTemplate,然后将pDoc加入到文档模板当中。
参考:1、MFC SDI单文档多视图的实现及自由切换(2种实现方式简析)
2、为啥交换窗口ID标识
3、MFC文档视图深入详解
4、深入分析MFC文档视图结构(项目实践)