MFC多文档界面视图架构解析
核心数据结构关系
- CWinApp 持有
CDocManager*指针,用于管理所有文档模板。 - CDocManager 维护一个
CPtrList列表,存储多个CMultiDocTemplate实例的指针。 - CMultiDocTemplate 保存三个运行时类指针:文档类、视图类和框架类,并关联资源标识符以加载界面布局。
- CDocument 包含指向其所属模板的指针(
m_pDocTemplate)以及一个视图集合(m_viewList),支持多视图共享同一文档。 - CFrameWnd 通过
m_pViewActive指向当前激活的视图。 - CView 通过
m_pDocument反向引用其所依附的文档对象。
注意:该结构中未内置框架窗口链表,若需创建多个独立框架,必须手动实现。
新建文档流程调用栈
- 在
InitInstance()中创建CMultiDocTemplate并注册到CWinApp::m_pDocManager。 - 用户触发
OnFileNew()后,调用CDocManager::OnFileNew()。 - 由
CMultiDocTemplate::OnDocumentFile()执行具体创建逻辑,参数lpszFileName用于判断是新建还是打开文件。
文档创建过程详解
- 调用
CreateNewDocument()基于m_pDocClass动态生成CDocument子类实例,并加入模板的文档列表。 - 调用
CreateNewFrame(pDocument, NULL)创建新的框架窗口,传入文档指针。 - 在框架窗口的
WM_CREATE消息处理中,通过LoadFrame()加载资源并创建客户区窗口。 - 在客户区中调用
CreateView()方法,利用m_pViewClass和资源 ID 构建视图,同时绑定文档对象。
同一文档的多视图实现方式
若需为同一文档创建多个视图窗口(同源),不能重复使用模板的 OpenDocumentFile,而应通过以下步骤:
- 获取当前活动子窗口:
CMDIChildWnd* pActiveChild = ((CMainFrame*)m_pMainWnd)->MDIGetActive(); - 提取其关联的文档指针:
CDocument* pDocument = pActiveChild->GetActiveDocument(); - 通过应用对象获取目标模板:
CMultiDocTemplate* pDocTemplate = ((CMT1App*)AfxGetApp())->m_pDocTemplate2; - 创建新框架窗口,传入已有文档和父窗口:
CFrameWnd* pFrame = pDocTemplate->CreateNewFrame(pDocument, pActiveChild); - 初始化新框架并显示视图:
pDocTemplate->InitialUpdateFrame(pFrame, pDocument);
关键代码片段(MT1.cpp)
// 在 InitInstance() 中注册两个不同视图模板
CMultiDocTemplate* pDocTemplate1 = new CMultiDocTemplate(
IDR_MT1TYPE,
RUNTIME_CLASS(CMT1Doc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CMT1View)
);
AddDocTemplate(pDocTemplate1);
m_pDocTemplate1 = pDocTemplate1;
CMultiDocTemplate* pDocTemplate2 = new CMultiDocTemplate(
IDR_MT1TYPE,
RUNTIME_CLASS(CMT1Doc),
RUNTIME_CLASS(CMyChildFrm2),
RUNTIME_CLASS(CMyView2)
);
AddDocTemplate(pDocTemplate2);
m_pDocTemplate2 = pDocTemplate2;
// 新建文档:创建新文档实例
void CMT1App::OnFileNew()
{
m_pDocTemplate1->OpenDocumentFile(NULL); // 创建新文档并添加至列表
}
// 多视图共享:基于现有文档创建新窗口
void CMT1App::OnFileNewMultiView()
{
CMDIChildWnd* pActiveChild = ((CMainFrame*)m_pMainWnd)->MDIGetActive();
if (!pActiveChild) return;
CDocument* pDocument = pActiveChild->GetActiveDocument();
if (!pDocument) return;
CFrameWnd* pFrame = m_pDocTemplate2->CreateNewFrame(pDocument, pActiveChild);
if (pFrame)
m_pDocTemplate2->InitialUpdateFrame(pFrame, pDocument);
}