文档/视图结构中的各个部分是如何联系到一起的

文档/视图结构是MFC中最有特色而又有难度的部分,在这当中涉及了应用、文档模板、文档、视图、MDI框架窗口、MDI子窗口等不同的对象,如果不了解这些部分之间如何关联的话,就可能犯错误,也就很难编出有水平的文档/视图程序。比如我在初学VC编程的时候,为应用程序添加了两个文档模板,两个模板公用一个文档类,只是视图不一样,期望当一个模板的文档的视图改变了文档后,调用UpdateAllViews后也能更新另一个文档模板的视图,结果当然是不行的,原因就是对MFC的文档/视图结构没有深入的了解,了解的最好方法就是阅读一下MFC的源代码。下面就是我的笔记:       (一)应用程序对象与文档模板之间的联系:      首先,在应用程序对象中有一个CDocManager指针类型的共有数据成员m_pDocManager,在CDocManager中维护一个CPtrList类型的链表:m_tempateList,它是一个保护成员。InitInstance函数中调用CWinApp::AddDocTemplate函数,实际上是调用m_pDocManager的AddDocTemplate函数向链表m_templateList添加模板指针。CWinApp提供了GetFirstDocTemplatePosition和GetNextDocTemplate函数实现对m_templateList链表的访问(实际上是调用了CDocManager的相关函数)。      在文件操作方面CWinApp提供的最常用的功能是文件的新建(OnFileNew)和打开(OnFileOpen),它也是调用CDocManager类的同名函数。对于新建,一般的时候在只有一个文档模板的时候,它新建一个空白的文件;如果有多个文档模板的时候,它会出现一个对话框提示选择文档类型。它的源代码如下:      void CDocManager::OnFileNew()      {      if (m_templateList.IsEmpty())      {      .......      return;      }      //取第一个文档模板的指针      CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();      if (m_templateList.GetCount() > 1)      {      // 如果多于一个文档模板,出现对话框提示用户去选择      CNewTypeDlg dlg(&m_templateList);      int nID = dlg.DoModal();      if (nID == IDOK)      pTemplate = dlg.m_pSelectedTemplate;      else      return;   // none - cancel operation      }      ......      //参数为NULL的时候OpenDocument File会新建一个文件      pTemplate->OpenDocumentFile(NULL);      }      打开文件:      void CDocManager::OnFileOpen()      {      // 出现打开文件对话框      CString newName;      if (!DoPromptFileName(newName, AFX_IDS_OPENFILE,      OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, TRUE, NULL))      return; // open cancelled      AfxGetApp()->OpenDocumentFile(newName);     //实际也是调用文档模板的同名函数      }       (二)文档模板与文档之间的联系:   从上面看出应用程序对象对文件的新建和打开是依靠文档模板的OpenDocumentFile函数实现的。MFC的模板类是用来联系文档类、视类和框架类的,在它的构造函数就需要这三者的信息:      CDocTemplate ( UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass );      构造函数利用后三个参数为它的三个CruntimeClass*类型的保护成员赋值:      m_pDocClass = pDocClass;      m_pFrameClass = pFrameClass;      m_pViewClass = pViewClass;      文档模板分为单文档模板和多文档模板两种,这两个模板的实现是不同的,除了上面的三个成员,内部有彼此不相同的但是很重要的成员变量。对于多文档模板:CPtrList m_docList;,单文档模板:CDocument* m_pOnlyDoc;。它们都有一个成员函数AddDocument,分别各自的成员进行赋值操作,而在它们的父类的CDocTemplate中则是为它所添加的文档的m_pDocTemplate变量赋值为模板自己的地址:      void CDocTemplate::AddDocument(CDocument* pDoc)      {      ASSERT_VALID(pDoc);      ASSERT(pDoc->m_pDocTemplate == NULL);      pDoc->m_pDocTemplate = this;      }      由于单文档模板只能拥有一个文档,所以它只是维护一个指向自己所拥有的模板的指针:m_pOnlyDoc,AddDocument函数就是要为这个成员赋值:      void CSingleDocTemplate::AddDocument(CDocument* pDoc)      {      ......      CDocTemplate::AddDocument(pDoc);      m_pOnlyDoc = pDoc;      }   由于多文档模板可以拥有多个文档,所以它要维护的是包含它所打开的所有文档的指针的链表,所以它的AddDocument的实现为:      void CMultiDocTemplate::AddDocument(CDocument* pDoc)      {      ......      CDocTemplate::AddDocument(pDoc);      m_docList..AddTail(pDoc);      }   模板通过m_pOnlyDoc(单文档)或记住了自己所拥有的所有的模板的指针,并通过GetFirstDocPosition和GetNextDoc函数可以实现对它所拥有的文档的访问,同时使文档记住了自己所属文档模板的指针,同时文档提供了GetDocTemplate()函数可以取得它所属的模板。      对AddDocument函数的调用主要是发生在另一个成员函数CreateNewDocument里,它的作用是创建一个新的文档:      CDocument* CDocTemplate::CreateNewDocument()      {      if (m_pDocClass == NULL)      {      ……      }      CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();      ……      AddDocument(pDocument);      return pDocument;      }      CreateNewDocument函数主要利用文档类的运行时指针的函数CreateObject创建一个新文档对象,并利用AddDocument将其指针赋給相关的成员,留做以后使用。      在应用程序的OnFileNew和OnFileOpen函数都使用了模板的OpenDocumentFile函数,而且在实际编程的时候也大都使用这个函数。在MSDN的文档说这个函数当参数不为NULL的时候打开文件,否则就用上面所说的CreateNewDocument函数创建一个新文档,那么它是如何实现的呢?      CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,      BOOL bMakeVisible)      {      CDocument* pDocument = NULL;      CFrameWnd* pFrame = NULL;      BOOL bCreated = FALSE;   // => doc and frame created      BOOL bWasModified = FALSE;      //如果已经有打开的文档,就会询问否保存文件      if (m_pOnlyDoc != NULL)      {      pDocument = m_pOnlyDoc;      if (!pDocument->SaveModified())      return NULL;      pFrame = (CFrameWnd*)AfxGetMainWnd();      ......      }      //创建新文件      else      {      pDocument = CreateNewDocument();      ASSERT(pFrame == NULL);      bCreated = TRUE;      }      ......      //如果第一次创建文档则也要创建框架窗口。      if (pFrame == NULL)      {      ASSERT(bCreated);      // create frame - set as main document frame      BOOL bAutoDelete = pDocument->m_bAutoDelete;      pDocument->m_bAutoDelete = FALSE;      pFrame = CreateNewFrame(pDocument, NULL);      pDocument->m_bAutoDelete = bAutoDelete;      ......      }      if (lpszPathName == NULL)      {      // 为新文档设置默认标题      SetDefaultTitle(pDocument);      ……      //一般的时候重载OnNewDocument初始化一些数据,如果返回FALSE,表示初始化失//败,销毁窗口。      if (!pDocument->OnNewDocument())      {      ......      if (bCreated)      pFrame->DestroyWindow();  // will destroy document      return NULL;      }      }      else      {      CWaitCursor wait;      // open an existing document      bWasModified = pDocument->IsModified();      pDocument->SetModifiedFlag(FALSE);      //OnOpenDocument函数重新初始化文档对象      if (!pDocument->OnOpenDocument(lpszPathName))      {      if (bCreated)      {      //新建文档的情况      pFrame->DestroyWindow();      }      else if (!pDocument->IsModified())      {      // 文档没
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值