MDI 应用程序是另一类重要的文档视结构程序。它的特点是:用户一次可以打开多个文档,每个文档对应不同的窗口;主窗口的菜单会自动随着当前活动的子窗口的变化而变化;可以对子窗口进行层叠、平铺等各种操作;子窗口可以在MDI 主窗口区域内定位、改变大小、最大化和最小化,当最大化子窗口时,它将占满MDI 主窗口的全部客户区。MDI 不仅可以在同一时间内同时打开多个文档,还可以为同一文档打开多个视图。在Windows 菜单下选择New ,就为当前活动文档打开一个新的子窗口。
从程序员角度看,每个MDI 应用程序必须有一个CMDWnd 或其派生类的实例,这个窗口称作MDI 框架窗口。CMDWnd 是CFrameWnd 的派生类,它除了拥有CFrameWnd 框架窗口类的全部特性外,还具有以下与MDI 相关的特性:
与SDI 不同,主框架窗口并不直接与一个文档和视图相关联。MDI 框架窗口拥有MDICLIENT (MDI 客户窗口),在显示或隐藏控制条(包括工具条、状态栏、对话条)时,重新定位该子窗口。
MDI 客户窗口是MDI 子窗口的直接父窗口,它负责管理主框架窗口的客户区以及创建子窗口。每个MDI 主框架窗口都有且只有一个MDI 客户窗口。
MDI 子窗口是CMDIChildWnd 或其派生类的实例,CMDIChildWnd 是CFrameWnd 的派生类,用于容纳视图和文档,它相当于SDI 下的主框架窗口。每打开一个文档,框架就自动为文档创建一个MDI 子窗口。一个MDI 应用程序负责动态的创建和删除MDI 子窗口。在任何时刻,最多只有一个子窗口是活动的( 窗口标题栏颜色呈高亮显示) 。MDI框架窗口始终与当前活动子窗口相关联,命令消息在传给MDI 框架窗口之前首先分派给当前活动子窗口。
在没有任何活动的MDI 子窗口时,MDI 框架窗口可以拥有自己的缺省菜单。当有活动子窗口时,MDI 框架窗口的菜单条会自动被子窗口的菜单所替代。框架会自动监视当前活动的子窗口类型,并相应的改变主窗口的菜单。比如,在Visual Studio 中,当选择对话框资源编辑窗口或源程序窗口时,菜单会有所不同。但是,对于程序员来说,只需要在InitInstance 中注册文档时指定每一类子窗口(严格的讲是文档)所使用的菜单,而不必显式的通过调用函数去改变主框架窗口的菜单,因为框架会自动完成这一任务。
MDI 框架窗口为层叠、平铺、排列子窗口和新建子窗口等一些标准窗口操作提供了缺省的菜单响应。在响应新建子窗口命令时,框架调用CDocTemplate::CreateNewFrame() 为当前活动文档创建一个子窗口。CreateNewFrame() 不仅创建子窗口,还创建与文档相对应的视图。
void CMainFrame::OnSecondMDI() //显示第二个子窗口
{
// TODO: Add your command handler code here
CMultiDocTemplate*pDocTemplate;
pDocTemplate=((CMultiMDIApp*)AfxGetApp())->pDocTemplate2;
// OnNewView(pDocTemplate); //第一中方法
pDocTemplate->OpenDocumentFile(NULL); //第二中方法,为NULL,则一个新文档被创建。该文档类型与这个模板相联系
//若想只打开一个窗口,点击后,只是把以前打开过的窗口给激活,两种方法
//第一种 文档遍历法
//第二种 设置变量法,在CChildFrame的构造函数中变量为1,析构中减1
}
BOOL CMainFrame::OnNewView(CMultiDocTemplate *pDocTemplate)
{
CMDIChildWnd* pActiveChild=MDIGetActive();
CDocument*pDocument;
if(pActiveChild==NULL||(pDocument=pActiveChild->GetActiveDocument())==NULL)
{
ASSERT(pDocTemplate!=NULL);
ASSERT(pDocTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate)));
pDocTemplate->OpenDocumentFile(NULL);
return TRUE;
}
ASSERT_VALID(pDocTemplate);
//创建一个包含 文档 和 视图 的框架窗口
CFrameWnd *pFrame=pDocTemplate->CreateNewFrame(pDocument,pActiveChild); //前面参数,新框架窗口所参照的文档,可取NULL值
//后面的参数。提供了新框架窗口所基于的模式
if(pFrame==NULL)
{
TRACE0("Warning:failed to create new frame\n");
return FALSE;
}
pDocTemplate->InitialUpdateFrame(pFrame,pDocument); //使框架窗口中的视图接收它们的OnInitialUpdate调用
return TRUE;
}