(Document Interface) 是Windows 界面的一种规范,它建立多个窗口来浏览文档数据,如Windows中的Program Manager 等都是按MDI 规范实现的。在实际工程软件开发中,许多程序员将其作为一种实现多窗口的标准方法。微软基础类库(Microsoft Foundation Class Library, 简称MFC 库), 是微软公司为方便Windows 程序开发所提供的一个功能强大的通用类库。MFC 的核心是以类的形式封装了大量Windows API。在可视化编程语言VC++ 下应用MFC是目前开发Windows 程序最方便的途径之一。VC++提供的各种开发工具如AppWizard、ClassWizard 和App Studio,可以建立起具备基本功能的Windows 框架程序(Framework)。而程序员所需要做的工作就是将自己特有的代码填入到框架程序中去,从而极大地减少了用户界面编程的工作量,加快了开发速度。关于MDI 的标准开发方法可参考一般的Windows 编程书籍,本文将介绍利用MFC 实现MDI 界面。
MFC 2.0 以上版本支持“ 文档/ 浏览视窗”(Document/View) 结构模式。由文档负责管理数据,浏览视窗负责数据显示及与用户的交互,从而实现了数据与界面的分离,使整个程序设计更具规范化、模块化。MFC 中,“ 文档” 由类CDocument 及其派生类实现( 简称Doc 类);“ 浏览视窗”由类CView 及其派生类实现 ( 简称View类)。二者都包含于应用程序的框架窗口中,并由其管理。使用单文档时,框架窗口由类CFrameWnd及其派生类实现;使用多文档时,框架窗口是利用类CMDIFrameWnd 和CMDIChildWnd实现。由文档模板将文档、浏览窗口和框架窗口三者联系起来。
当程序员在App Wizard 的Option 选项中选择 Multiple Document Interface 时,MFC 构架程序(Framework) 将自动生成实现MDI 基本功能的代码。类CMDIFrameWnd 负责整个应用程序的主框架窗口;类CMDIChildWnd 实现MDI的子窗口框架,它不带菜单项,而与主框架窗口共享菜单。主框架窗口依据当前激活的子窗口自动更换菜单项。CView 则负责MDI 子窗口客户区中显示的具体内容。例如,App Wizard 的以M01 为Project 名建立的构架程序(framework) 中包括一些基本类:主框架窗口CMainFrame:派生自CMDIFrameWnd;文档CM01Doc:派生自CDocument;浏览窗口CM01View:派生自CView;其中CM01Doc、CM01View和CMDIChildWnd 由多文档模板CMultiDocTemplate 联系在一起。在CM01App::InitInstance()函数中代码如下:
BOOL CM01App::InitInstance()
{
......
CMultiDocTemplate* pDocTemplate;
// CMultiDocTemplate 用 于MDI 文 档
pDocTemplate = new CMultiDocTemplate(
IDR_M01TYPE,// 资 源 标 识
RUNTIME_CLASS(CM01Doc),
// 文 档 类
RUNTIME_CLASS(CMDIChildWnd),
// 标 准MDI 子 窗 口 框 架
RUNTIME_CLASS(CM01View));
// 浏 览 视 窗 类
AddDocTemplate(pDocTemplate);
// 为 整 个 应 用 程 序 添 加 新 模 板
......
}
此时,数据Doc 类仅与一种View 类相关联,MDI 每个子窗口显示的内容是一致的。如果用户希望不同的子窗口显示不同的文档,则需要分别建立新的资源项、新的文档类、新的View 类,并且用新模板将他们与CMDIChildWnd 联系起来即可。MFC 框架程序将复杂的消息发送和接收机制隐藏起来,自动实现子窗口的调度安排。程序员只需设定自己的数据,并在各个View中重载OnDraw() 函数,完成所需的绘制。
然而在实际开发应用程序中,常常希望对某一类数据进行不同方式的显示,既可观察数值,又可有图形显示。这就要求同一种Doc 类与多个View 类相关联,而每个View类对应一个不同的MDI 子窗口。CMultiDocTemplate 的典型用法是建立独立的文档结构和View对象。而下面CMultiDocTemplate 将使用同一文档和多个View 类。
(1) 用ClassWizard 建立一新的View 类:CM02View。
(2) 建立新模板:
CMultiDocTemplate* pDocTemplate02=new CMultiDocTemplate(
IDR_M01TYPE, // 使用同一资源
RUNTIME_CLASS(CM01Doc), // 同一文档
RUNTIME_CLASS(CMDIChildWnd), // 标准MDI 子窗口框架
RUNTIME_CLASS(CM02View)); // 新View
然后使用CApp::AddDocTemplate 函数添加新模板。
如果此时仍然在CM01App::InitInstance() 函数中添加新模板,则构架程序会错误地认为程序支持两种文档类型,从而在编译产生的EXE 文件执行时弹出对话框,要求用户选择文档类型。而实际上两种文档类型是一样的。
为避免此种情况,可使用MFC 开发者建议的方法:在前例情况下,首先,应在App Studio中将字串资源IDR_M01TYPE复制为一个新字串资源IDR_M02TYPE。然后,删去字串资源IDR_M02TYPE 中第二个后的字符串M01 Document( 该字串即为CDocTemplate::fileNewName项)。之后,用新资源IDR_M02TYPE 来建立第二个模板。这样编译的EXE文件将不会弹出对话框。在研究MFC 的源码之后,发现之所以弹出文档类型对话框,是由于CM01App::InitInstance() 函数中调用了OnFileNew()函数。OnFileNew()函数检查文档模板数量;当不止一个模板时,则弹出对话框;待用户选择之后,按所选的文档类型建立MDI 窗口。由于删去了第二个模板的fileNewName 项,无法显示文档类型,就自动停止对话框,而将第一种类型作为缺省文档类型建立MDI 窗口。
在工程应用程序中,OnFileNew() 函数一般只在程序初始化时调用一次( 至于菜单File |New的响应,用户可接管处理),所以可以不在CMyApp::InitInstance() 函数中添加新文档模板,躲过OnFileNew() 函数的检查,而在需要的时候添加所需的文档模板,建立新的子窗口。这样既避免了文档类型对话框,又不必增加字串资源。
一种简单的例子如下:第一个子窗口仍由构架程序自动建立;设定一个新的菜单项“新窗口(NewWindow)”,在CMainFrame 中处理该菜单消息,消息响应函数中显示第二个子窗口。
void CMainFrame::OnNewWindow()
{
// 添 加 新 的 文 档 模 板
static CMultiDocTemplate* pDocTemplate_New;
static BOOL bChildCreated=FALSE;
// 标 志, 新 窗 口 是 否 建 立; 如 已 建, 将 不 重 建
if(bChildCreated==FALSE)
{
pDocTemplate_New = new CMultiDocTemplate(
IDR_M01TYPE, // 使 用 同 一 资 源
RUNTIME_CLASS(CM01Doc),
RUNTIME_CLASS(CMDIChildWnd),
// 标 准MDI 子 窗 口 框 架
RUNTIME_CLASS(CM02View));
AfxGetApp()->AddDocTemplate(pdocTemplate_New);
// 创 建 新 的 子 窗 口
CMDIChildWnd* pMDIActive = MDIGetActive(); // 获 得 当 前 活 动 子 窗 口 的 指 针
CMpvDoc* pDoc = (CMpvDoc*)pMDIActive->GetActiveDocument(); // 获 得 文 档 指 针
CMDIChildWnd* pNewFrame=(CMDIChildWnd*) (pDocTemplate_New ->CreateNewFrame(pDoc, NULL));
// 建 立 新 的 框 架 窗 口
if (pNewFrame == NULL)
{
AfxMessageBox(" 新 窗 口 不 能 建 立",MB_OK,0);
return; // not created
}
pDocTemplate_New ->InitialUpdateFrame(pNewFrame, pDoc); // 显 示 窗 口
MDITile(MDITILE_HORIZONTAL); // 将 多 个 窗 口 平 铺
bChildCreated=TRUE;
}
不 同 的View 在OnDraw() 函 数 中 有 各 自 的 绘 制 代 码, 当 数 据 更 新 时, 只 要 调 用CDocument::UpdateAllViews() 函 数, 即 可 更 新 全 部 的MDI 子 窗 口。
以 上 所 讨 论 的 程 序 在Windows 3.1、 中 文 之 星2.0、 VC++ 1.52、MFC 2.50 环 境 下 通 过。 从 中可 以 看 到: 利 用MFC 实 现MDI, 将 复 杂 的 多 窗 口 安 排 交 给 框 架 程 序(Framework) 来 承 担, 编 程 人 员可 将 精 力 集 中 于 自 己 特 有 的 任 务, 极 大 地 提 高 了 编 程 的 效 率。