首页 > 代码库 > [转载]MFC一个文档不同视图

[转载]MFC一个文档不同视图

  MDI(MultipleDocument Interface)是Windows界面的一种规范,它建立多个窗口来浏览文档数据,如Windows中的ProgramManager等都是按MDI规范实现的。在实际工程软件开发中,许多程序员将其作为一种实现多窗口的标准方法。微软基础类库(MicrosoftFoundationClassLibrary,简称MFC库),是微软公司为方便Windows程序开发所提供的一个功能强大的通用类库。MFC的核心是以类的形式封装了大量WindowsAPI。在可视化编程语言VC++下应用MFC是目前开发Windows程序最方便的途径之一。VC++提供的各种开发工具如AppWizard、ClassWizard和AppStudio,可以建立起具备基本功能的Windows框架程序(Framework)。而程序员所需要做的工作就是将自己特有的代码填入到框架程序中去,从而极大地减少了用户界面编程的工作量,加快了开发速度。关于MDI的标准开发方法可参考一般的Windows编程书籍,本文将介绍利用MFC实现MDI界面。

  MFC2.0以上版本支持“文档/浏览视窗”(Document/View)结构模式。由文档负责管理数据,浏览视窗负责数据显示及与用户的交互,从而实现了数据与界面的分离,使整个程序设计更具规范化、模块化。MFC中,“文档”由类CDocument及其派生类实现(简称Doc类);“浏览视窗”由类CView及其派生类实现(简称View类)。二者都包含于应用程序的框架窗口中,并由其管理。使用单文档时,框架窗口由类CFrameWnd及其派生类实现;使用多文档时,框架窗口是利用类CMDIFrameWnd和CMDIChildWnd实现。由文档模板将文档、浏览窗口和框架窗口三者联系起来。

  当程序员在AppWizard的Option选项中选择MultipleDocumentInterface时,MFC构架程序(Framework)将自动生成实现MDI基本功能的代码。类CMDIFrameWnd负责整个应用程序的主框架窗口;类CMDIChildWnd实现MDI的子窗口框架,它不带菜单项,而与主框架窗口共享菜单。主框架窗口依据当前激活的子窗口自动更换菜单项。CView则负责MDI子窗口客户区中显示的具体内容。例如,AppWizard的以M01为Project名建立的构架程序(framework)中包括一些基本类:主框架窗口CMainFrame:派生自CMDIFrameWnd;文档CM01Doc:派生自CDocument;浏览窗口CM01View:派生自CView;其中CM01Doc、CM01View和CMDIChildWnd由多文档模板CMultiDocTemplate联系在一起。在CM01App::InitInstance()函数中代码如下:

 1 BOOL CM01App::InitInstance() 2 { 3   ...... 4  5   CMultiDocTemplate*pDocTemplate; 6   //CMultiDocTemplate用于MDI文档 7   pDocTemplate=newCMultiDocTemplate( 8   IDR_M01TYPE,//资源标识 9   RUNTIME_CLASS(CM01Doc),10   //文档类11   RUNTIME_CLASS(CMDIChildWnd),12   //标准MDI子窗口框架13   RUNTIME_CLASS(CM01View));14   //浏览视窗类15   AddDocTemplate(pDocTemplate);16   //为整个应用程序添加新模板17   ......18 }

  此时,数据Doc类仅与一种View类相关联,MDI每个子窗口显示的内容是一致的。如果用户希望不同的子窗口显示不同的文档,则需要分别建立新的资源项、新的文档类、新的View类,并且用新模板将他们与CMDIChildWnd联系起来即可。MFC框架程序将复杂的消息发送和接收机制隐藏起来,自动实现子窗口的调度安排。程序员只需设定自己的数据,并在各个View中重载OnDraw()函数,完成所需的绘制。

  然而在实际开发应用程序中,常常希望对某一类数据进行不同方式的显示,既可观察数值,又可有图形显示。这就要求同一种Doc类与多个View类相关联,而每个View类对应一个不同的MDI子窗口。CMultiDocTemplate的典型用法是建立独立的文档结构和View对象。而下面CMultiDocTemplate将使用同一文档和多个View类。

(1)用ClassWizard建立一新的View类:CM02View。

(2)建立新模板:

CMultiDocTemplate*pDocTemplate02=newCMultiDocTemplate(

  IDR_M01TYPE,//使用同一资源

  RUNTIME_CLASS(CM01Doc),//同一文档

  RUNTIME_CLASS(CMDIChildWnd),//标准MDI子窗口框架

  RUNTIME_CLASS(CM02View));//新View

  然后使用CApp::AddDocTemplate函数添加新模板。

  如果此时仍然在CM01App::InitInstance()函数中添加新模板,则构架程序会错误地认为程序支持两种文档类型,从而在编译产生的EXE文件执行时弹出对话框,要求用户选择文档类型。而实际上两种文档类型是一样的。

  为避免此种情况,可使用MFC开发者建议的方法:在前例情况下,首先,应在AppStudio中将字串资源IDR_M01TYPE复制为一个新字串资源IDR_M02TYPE。然后,删去字串资源IDR_M02TYPE中第二个后的字符串M01Document(该字串即为CDocTemplate::fileNewName项)。之后,用新资源IDR_M02TYPE来建立第二个模板。这样编译的EXE文件将不会弹出对话框。在研究MFC的源码之后,发现之所以弹出文档类型对话框,是由于CM01App::InitInstance()函数中调用了OnFileNew()函数。OnFileNew()函数检查文档模板数量;当不止一个模板时,则弹出对话框;待用户选择之后,按所选的文档类型建立MDI窗口。由于删去了第二个模板的fileNewName项,无法显示文档类型,就自动停止对话框,而将第一种类型作为缺省文档类型建立MDI窗口。

  在工程应用程序中,OnFileNew()函数一般只在程序初始化时调用一次(至于菜单File|New的响应,用户可接管处理),所以可以不在CMyApp::InitInstance()函数中添加新文档模板,躲过OnFileNew()函数的检查,而在需要的时候添加所需的文档模板,建立新的子窗口。这样既避免了文档类型对话框,又不必增加字串资源。

  一种简单的例子如下:第一个子窗口仍由构架程序自动建立;设定一个新的菜单项“新窗口(NewWindow)”,在CMainFrame中处理该菜单消息,消息响应函数中显示第二个子窗口。

 1 Void CMainFrame::OnNewWindow() 2 { 3      //添加新的文档模板 4     Static CMultiDocTemplate*pDocTemplate_New; 5     Static BOOLbChildCreated=FALSE; 6     //标志,新窗口是否建立;如已建,将不重建 7   if(bChildCreated==FALSE) 8   { 9     pDocTemplate_New=new CMultiDocTemplate(10     IDR_M01TYPE,//使用同一资源11     RUNTIME_CLASS(CM01Doc),12     RUNTIME_CLASS(CMDIChildWnd),13     //标准MDI子窗口框架14     RUNTIME_CLASS(CM02View));15     AfxGetApp()->AddDocTemplate(pdocTemplate_New);16     //创建新的子窗口17     CMDIChildWnd* pMDIActive=MDIGetActive();//获得当前活动子窗口的指针18     CMpvDoc* pDoc=(CMpvDoc*)pMDIActive->GetActiveDocument();//获得文档指针19     CMDIChildWnd* pNewFrame=(CMDIChildWnd*)(pDocTemplate_New->CreateNewFrame(pDoc,NULL));20     //建立新的框架窗口21     if(pNewFrame==NULL)22     {23       AfxMessageBox("新窗口不能建立",MB_OK,0);24       return;//notcreated25     }26     pDocTemplate_New->InitialUpdateFrame(pNewFrame,pDoc);//显示窗口27     MDITile(MDITILE_HORIZONTAL);//将多个窗口平铺28     bChildCreated=TRUE;29   }30 }

  不同的View在OnDraw()函数中有各自的绘制代码,当数据更新时,只要调用CDocument::UpdateAllViews()函数,即可更新全部的MDI子窗口。

 

Collapse image概要

有时是想创建一个基于 CMultiDocTemplate 的窗口 (换句话说,CFrame/CDocument/CView 组合) 而无需使用由 CWinApp::OnFileNew 提供的机制。
例如,如果程序有多个文档模板,则 CWinApp::OnFileNew 将询问哪种类型的文档,以打开一个对话框提示用户。程序员可能已经知道哪种类型的 CMultiDocTemplate 来使用,并因此可能会不想提示用户,因为它将是不适当的应用程序的给定的上下文中。

Collapse image更多信息

未记录的 CMultiDocTemplate::OpenDocumentFile 函数假定应用程序最初创建与应用程序向导,用于创建一个新的基于 CMultiDocTemplate 窗口。有涉及的几个步骤:
  1. CWinApp 派生的类中添加的 CMultiDocTemplate 指针:
          class CMyApp : public CWinApp      {        ...       public:        CMultiDocTemplate* m_pDocTemplate;        ...      }						
    注意: 如果您计划使用多个文档类型,则必须创建每种文档类型的 CMultiDocTemplate 指针成员变量。
  2. 在对 CWinApp::InitInstance 的调用,从 AddDocTemplate 到调用中移除创建的 CMultiDocTemplate。将指针设置为指向新的 CMultiDocTemplate。使用指针调用 AddDocTemplate:
          BOOL CMyApp::InitInstance()      {        ...        m_pDocTemplate = new CMultiDocTemplate(IDR_TEXTTYPE,                                      RUNTIME_CLASS(CMyDoc),                                      RUNTIME_CLASS(CMDIChildWnd),                                      RUNTIME_CLASS(CMyView));        AddDocTemplate(m_pDocTemplate);        ...      }						
  3. 使用指针调用 CMultiDocTemplate::OpenDocumentFile 具有空参数来创建新的窗口。对于此示例,假定在 CView 窗口中没有一个按钮。在按钮的 BN_CLICKED 处理,我们要创建一个基于 m_pDocTemplate 的窗口:
          void CMyView::OnNewWindowButtonClicked()      {          CMyApp* pApp = (CMyApp*)AfxGetApp();          pApp->m_pDocTemplate->OpenDocumentFile(NULL);      }						
    此技术可用于单文档界面 (SDI) 应用程序中创建一个基于 CSingleDocTemplate 的窗口。但是,它不是必要的。由于只有一个应用程序的文档模板,调用 OnFileNew() 将创建而不会提示用户输入的文档类型的新窗口。
  4. 参考文献 http://support.microsoft.com/kb/113257/zh-cn

[转载]MFC一个文档不同视图