CDocTemplate::CreateNewFrame调用MDI子窗口类(CMDIChildWnd)的构造函数,生成MDI子窗口对象。接着调用CMDIChildWnd::PreCreateWindow。然后,生成一个CCreateContext对象,(CcreateContext是MFC框架所使用的一种结构,它将构成文档和视的组件联系起来。后文将详细介绍之。)并将该对象值传给CMDIChildWnd::OnCreateClient函数。MFC调用此函数,用CCreateContext对象提供的信息创建一个或多个CView对象。此时,各视的构造函数被依次调用。
(3)、接着,判断lpszPathName是否为空。分为两种情况:
(a)、若为空,则表明要创建一个新文档:调用SetDefaultTitle函数装载文档的缺省标题,并显示在文档的标题栏中;然后执行CDocument::OnNewDocument。该函数调用DeleteContents以保证文档为空,然后置新文档为清洁。可以重载该函数。
(b)、否则,表明要打开一个已存在的文档:调用CDocument::OnOpenDocument打开指定的文件;执行DeleteContext,保证文档为空;调用CObject::Serialize读入该文件的内容。(程序员可在此进行文件的读入操作。当然,也可以在CDocument::OnOpenDocument中读入文件)。然后置文档为清洁;最后,调用CDocTemplate::SetPathName,并把文件名加入到最近文件列表中。
(4)、调用CDocTemplate::InitialUpdateFrame函数,使框架窗口中的各个视收到OnInitialUpdate调用。框架窗口的主视(子窗ID等于AFX_IDW_PANE_FIRST的视)被激活。程序员可以在此对视对象进行初始化。
(2)、Window/New命令的程序流程
当主框架窗口上有子窗口时,选择Window/New命令可以生成该活动子窗口的影象。它们有相同的文档模板、相同的文档。其流程如下:
执行Window/New的过程与File/New的过程差不多。所不同的是,File/New须要创建一个新文档,而Window/New则是获得已存在的MDI子窗口的文档。因此以前存在的视和New以后生成的视均为该文档的视,都是该文档的内容的显示。当调用CDocument::UpdateAllViews函数时,它们(视)的OnUpdate函数都将被激活。此时,在该文档的视指针列表中,将有多于一个的视(具体数目视Window/New执行的次数而定)。读者可以利用(图3)中的代码跟踪程序结果。
(四)、几种情况的讨论
上面,笔者就MFC中文档/视的关系进行了分析,下面,笔者将结合具体情况进行讨论:
(1)、如何根据自己的要求来选择文档模板,及相应的视和文档。
在通常的MDI应用程序中,只有一个文档模板,程序员只能打开一种类型的文档。因此,程序员只要调用File/New或者File/Open创建或者打开文档即可,至于文档、视和框架窗口之间的关系,由文档模板在幕后控制,不须要对文档模板进行操作。但是,如果应用程序需要处理多种类型的文档,并且何时打开何种文档均需程序员手工控制,此时,程序员必须对文档模板进行编程。
例如,笔者需要处理AVI和BMP两种文件类型。AVI和BMP的数据存放格式不同,不能用同一的数据结构来描述,因此,把它们的数据都存入一个文档是不合适的。同时,由于AVI是图象序列,BMP仅是一幅图象,它们的显示是肯定不一样的,即它门的视不同。基于此,笔者决定分别建立两套文档模板,两套框架窗口,两套文档和两套视,分别用于AVI和BMP的数据存放和显示。程序可以根据用户选择的文件名来分别处理AVI和BMP。具体步骤如下:
(Step 1)、在应用程序类(CWinApp)的派生类中增加文档模板成员变量,以便对文档模板进行操作。
class C3dlcsApp : public CWinApp
{ 。。。 。。。
public:
CMultiDocTemplate * m_pAVIDocTemplate;
CMultiDocTemplate * m_pBMPDocTemplate;
}
(Step 2)、在主框架中增加菜单响应:
void CMainFrame::OnFileOpen() {
CFileDialog my(true);
if(my.DoModal()==IDOK) {
CString Fil