【CmultiDocTemplate】document template MFC的document/view结构
2012.9.5 多文档 CmultiDocTemplate()
CMultiDocTemplate::CMultiDocTemplate
CMultiDocTemplate(
UINT nIDResource,
CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass,
CRuntimeClass* pViewClass );
CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass,
CRuntimeClass* pViewClass ,
UINT *arID,int n): CMultiDocTemplate(nIDResource,pDocClass,pFrameClass,pViewClass)
{
}
nIDResource:构造函数的第一个参数是一个资源ID,该资源用于提供该文档类型的菜单、快捷键、按钮等。
CRuntimeClass* pDocClass, //文档
CRuntimeClass* pFrameClass, //包含视的窗口
CRuntimeClass* pViewClass //与上面文档关联的视
//example for CMultiDocTemplate
BOOL CMyApp::InitInstance()
{
// ...
// Establish all of the document types
// supported by the application
AddDocTemplate( new CMultiDocTemplate( IDR_SHEETTYPE,
RUNTIME_CLASS( CSheetDoc ),
RUNTIME_CLASS( CMDIChildWnd ),
RUNTIME_CLASS( CSheetView ) ) );
AddDocTemplate( new CMultiDocTemplate( IDR_NOTETYPE,
RUNTIME_CLASS( CNoteDoc ),
RUNTIME_CLASS( CMDIChildWnd ),
RUNTIME_CLASS( CNoteView ) ) );
// ...
}
Here is a second example.
BOOL CYourApp::InitInstance()
{
// Normally, an application creates a document
// template and registers it with MFC as a part
// of its initialization.
// IDR_SAMPLERESOURCE is a resource ID string; see
// the CDocTemplate class overview documentation
// for more information on its format.
// The next three parameters use the RUNTIME_CLASS()
// macro to get runtime type information for the doc,
// frame, and view classes that will be associated
// by the template.
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_SAMPLERESOURCE,
RUNTIME_CLASS(CYourDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CYourViewClass));
// After the following call, MFC is aware of the doc
// template and will free it when the application is
// shut down. The doc templates known to MFC will
// automatically be used when CWinApp:OnFileOpen()
// or CWinApp::OnFileNew() are called.
AddDocTemplate(pDocTemplate);
// You might have other initialization code ...
// ...
return TRUE;
}
Document即为“资料”,按我理解就是饭店的厨师;而View就是饭店的服务员。View负责点菜和上菜(对用户请求做出直接响应),而Document负责烹饪,即处理用户的要求。
除了Document和View,还有一个Frame,因为View要放在Frame内部,Frame就是承载View的框架。而三者之间的关系是由Document Template来管理的,一份Document Template管理一个document\frame\view三件组,而一个程序可以有多个document template,多个document template由一个CDocManager对象管理。
document template
一个MDI(多文档接口)应用程序使用主框架窗口(main frame window)作为工作区,在工作区里用户可以打开多个文档框架窗口,每一个文档框架窗口用以显示一份文档。
Document template是用来定义以下三种类之间关系的模板:
Document(文档)类: 从CDocument派生而来,用于处理数据,即所谓数据之体。
View(视图)类: 用于将来自Document类的数据显示出来,可以从CView、CScrollView、CFormView和CEditView类派生,也可以直接使用CEditView类。
frame window框架窗口类:用以包含View。对于MDI程序,可以从CMDIChildWnd派生,也可以直接使用该类。
MDI应用程序可以支持不止一种文档,而且不同种类的文档可以同时打开(比如一个text和一个bitmap)。对于每一种所支持的文档,应用程序都应该有一份对应的document template进行管理。也就是说你的应用程序支持几种文档,就应该有几个Document template。
当用户创建新文档的时候,应用程序就会使用document template。如果程序支持的文档种类在一种以上,那么程序框架就会从document templates处取得所有的文档类型名字,显示在File New对话框里。一旦用户选择了文档类型,应用程序就会创建一个document对象,一个frame window对象和一个view对象,并且将它们联系在一起(通过document template)。
通常程序员不需要使用CMultiDocTemplate的任何成员函数(除了构造函数外)。框架会在内部自动处理CMultiDocTemplate对象。
为了管理通过相关view对象和frame window对象来构建document的复杂过程,framework使用两种document template类:
CSingleDocTemplate类用于SDI程序;CMultiDocTemplate类用于MDI程序。一个CSingleDocTemplate在同一时刻只能创建并储存单一种类的一个文档;一个CMultiDocTemplate在同一时刻可以管理单一种类的多个文档。
有些应用程序支持不止一种文档类型,比如同时支持文本和图形。这种应用程序为每个支持的类型使用单独的document template对象,见下图:
这个应用程序支持两种文档类型,因此具备两个document template对象。对于每一种文档类型可以打开多个文档,每打开一个文档应用程序就为之创建三个对象:CMyDocument对象用于处理数据,CMyView对象用于显示,CMyFrameWnd用于装载view,但是不管打开多少个同类型文档,负责管理该类型的document template对象只有一个,它负责管理的是上述三个类之间的关系,负责在这三个类的对象创建之时指定它们之间的关系。
上面说到每打开一个document,会随之一起创建一个view和一个frame window,而这三者的创建工作就是由document template完成的,当用户点击“File/New”或者“File/Open”后,消息发出,被theApp的OnFileNew()接到,但它经过一系列的调用(比较绕)最终调用的是CMultiDocTemplate::OpenDocumentFile(),该函数完成此三对象的创建,其中view的创建又是非常的绕,最终经过一系列的调用由CFrameWnd::CreateView()完成,另外还会调用CView从CWnd继承来的函数Create()用于产生与该view对应的真实窗口。而创建什么种类的document、window、view是在创建document template时由document template的构造函数的参数指定的。
下面显示了创建一个CMultiDocTemplate(用以管理MDI的document template)的过程:
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_CMyDocTypeTYPE,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CMyView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
IDR_CMyDocTypeTYPE:传给构造函数的第一个参数是一个资源ID,该资源用于提供该文档类型的菜单、快捷键、按钮等。
剩余三个参数用RUN_CLASS()宏提供CMultiDocTemplate创建document\window\view时所需要的类型信息(即对应的RuntimeClass对象,当用户打开一个文件时,document template就可以据此动态创建出document\window\view,这就很好体现了MFC动态创建的用途,关于动态创建是由DECLARE_DYNCREATE()\IMPLEMENT_DYNCREATE()宏实现的),最后用AddDocTemplate()加载此document template,AddDocTemplate()实际上是将document template加到由theApp的一个指针CDocManager* m_pDocManager所维护的document template链表中CDocTemplate有三个成员变量分别持有document\window\view的RuntimeClass对象的指针,另外还有一个资源ID成员。
Document template对象是被theApp创建的。在theApp的InitInstance()中的一个关键任务就是创建一个或多个适当种类的document template。theApp会在template list中保存指向每一个document template的指针并提供一个接口用于增加document template(AddDocTemplate())。如果你想要支持两个或以上的文档类型,你必须为每个文档类型显式地调用AddDocTemplate()。
多个Document template是由一个CDocManager对象管理的,很多原本由CWinApp做的关于document template的工作如:AddDocTemplate()、OpenDocumentFile()、NewDocumentFile(),在MFC4.0后都由CDocManager来做了。
CDocTemplate\CDocment\CFrameWnd\CView之间的指针互指关系
列出:
CDocTemplate有指向其余三者RuntimeClass对象的指针:
CRuntimeClass* m_pDocClass;
CRuntimeClass* m_pFrameClass;
CRuntimeClass* m_pViewClass;
还有指向Document列表的指针:CPtrList m_pDocList;表示一个CDocTemplate可以维护多个同类型文档。
CDocument有CDocTemplate* m_pDocTemplate回指CDocTemplate;另有CPtrList m_pViewList指向一个view的链表,表示一个Document可以对应多个View。
CFrameWnd有CView* m_pViewActive指向当前活动在其中的view。
CView有CDocument* m_pDocument指向对应的Document。
CDocument\CFrameWnd\CView之间互相操作的函数
CDocument::UpdateAllViews()—————>CView:OnUpdate()
CView::GetDocument();
CView::GetParentFrame();
CFrameWnd::GetActiveView();
CFrameWnd::GetActiveDocument();
View和Document的通信
程序员通过改写CMyView的如下函数达到View和Document通信的目的:
CView::OnInitialUpdate():负责view的初始化。
CView::OnUpdate():Frameword在Document发生变化时调用此函数,此为预留给程序员的“用Document的变化指导View”的接口。
CView::OnDraw():该函数作为WM_PAINT的间接响应,负责View的更新。
CDocument::UpdateAllViews()/CView::OnUpdate()这一对函数是命令与执行的关系,调用UpdateAllViews()就会通知所有的View,通知方法就是调用其OnUpdate()。