科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件深入浅出MFC文档/视图架构之文档模板

深入浅出MFC文档/视图架构之文档模板

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在\\\"文档/视图\\\"架构的MFC程序中,提供了文档模板管理者类CDocManager,由它管理应用程序所包含的文档模板

作者:宋宝华 来源:天极开发 2007年10月16日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
CDocTemplate类的AddDocument、RemoveDocument成员函数使得CDocument* pDoc参数所指向的文档归属于本文档模板(通过将this指针赋值给pDoc所指向CDocument对象的m_pDocTemplate成员变量)或脱离与本文档模板的关系:

void CDocTemplate::AddDocument(CDocument* pDoc)
{
 ASSERT_VALID(pDoc);
 ASSERT(pDoc->m_pDocTemplate == NULL); // no template attached yet
 pDoc->m_pDocTemplate = this;
}
void CDocTemplate::RemoveDocument(CDocument* pDoc)
{
 ASSERT_VALID(pDoc);
 ASSERT(pDoc->m_pDocTemplate == this); // must be attached to us
 pDoc->m_pDocTemplate = NULL;
}

  而CDocTemplate类的CreateNewDocument成员函数则首先调用CDocument运行时类的CreateObject函数创建一个CDocument对象,再调用AddDocument成员函数将其归属于本文档模板类:

CDocument* CDocTemplate::CreateNewDocument()
{
 // default implementation constructs one from CRuntimeClass
 if (m_pDocClass == NULL)
 {
  TRACE0("Error: you must override CDocTemplate::CreateNewDocument.\n");
  ASSERT(FALSE);
  return NULL;
 }
 CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
 if (pDocument == NULL)
 {
  TRACE1("Warning: Dynamic create of document type %hs failed.\n",m_pDocClass->m_lpszClassName);
  return NULL;
 }
 ASSERT_KINDOF(CDocument, pDocument);
 AddDocument(pDocument);
 return pDocument;
}

  文档类对象由文档模板类构造生成,单文档模板类CSingleDocTemplate只能生成一个文档类对象,并用成员变量 m_pOnlyDoc 指向该对象;多文档模板类可以生成多个文档类对象,用成员变量 m_docList 指向文档对象组成的链表。

  CSingleDocTemplate的构造函数、AddDocument及RemoveDocument成员函数都在CDocTemplate类相应函数的基础上增加了对m_pOnlyDoc指针的处理:

CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource,
CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass,
CRuntimeClass* pViewClass)
: CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)
{
 m_pOnlyDoc = NULL;
}
void CSingleDocTemplate::AddDocument(CDocument* pDoc)
{
 ASSERT(m_pOnlyDoc == NULL); // one at a time please
 ASSERT_VALID(pDoc);

 CDocTemplate::AddDocument(pDoc);
 m_pOnlyDoc = pDoc;
}
void CSingleDocTemplate::RemoveDocument(CDocument* pDoc)
{
 ASSERT(m_pOnlyDoc == pDoc); // must be this one
 ASSERT_VALID(pDoc);

 CDocTemplate::RemoveDocument(pDoc);
 m_pOnlyDoc = NULL;
}

  同样,CMultiDocTemplate类的相关函数也需要对m_docList所指向的链表进行操作(实际上AddDocument和RemoveDocument成员函数是文档模板管理其所包含文档的函数):

// CMultiDocTemplate document management (a list of currently open documents)
void CMultiDocTemplate::AddDocument(CDocument* pDoc)
{
 ASSERT_VALID(pDoc);

 CDocTemplate::AddDocument(pDoc);
 ASSERT(m_docList.Find(pDoc, NULL) == NULL); // must not be in list
 m_docList.AddTail(pDoc);
}
void CMultiDocTemplate::RemoveDocument(CDocument* pDoc)
{
 ASSERT_VALID(pDoc);

 CDocTemplate::RemoveDocument(pDoc);
 m_docList.RemoveAt(m_docList.Find(pDoc));
}

  由于CMultiDocTemplate类可包含多个文档,依靠其成员函数GetFirstDocPosition和GetNextDoc完成对文档链表m_docList的遍历:

POSITION CMultiDocTemplate::GetFirstDocPosition() const
{
 return m_docList.GetHeadPosition();
}
CDocument* CMultiDocTemplate::GetNextDoc(POSITION& rPos) const
{
 return (CDocument*)m_docList.GetNext(rPos);
}

  而CSingleDocTemplate的这两个函数实际上并无太大的意义,仅仅是MFC要玩的某种"招数",这个"招数"高明吗?相信看完MFC的相关源代码后你或许不会这么认为,实际上CSingleDocTemplate的GetFirstDocPosition、GetNextDoc函数仅仅只能判断m_pOnlyDoc的是否为NULL:

POSITION CSingleDocTemplate::GetFirstDocPosition() const
{
 return (m_pOnlyDoc == NULL) ? NULL : BEFORE_START_POSITION;
}

CDocument* CSingleDocTemplate::GetNextDoc(POSITION& rPos) const
{
 CDocument* pDoc = NULL;
 if (rPos == BEFORE_START_POSITION)
 {
  // first time through, return a real document
  ASSERT(m_pOnlyDoc != NULL);
  pDoc = m_pOnlyDoc;
 }
 rPos = NULL; // no more
 return pDoc;
}

  笔者认为,MFC的设计者们将GetFirstDocPosition、GetNextDoc作为基类CDocTemplate的成员函数是不合理的,一种更好的做法是将GetFirstDocPosition、GetNextDoc移至CMultiDocTemplate派生类。

  CDocTemplate还需完成对其对应文档的关闭与保存操作:

BOOL CDocTemplate::SaveAllModified()
{
 POSITION pos = GetFirstDocPosition();
 while (pos != NULL)
 {
  CDocument* pDoc = GetNextDoc(pos);
  if (!pDoc->SaveModified())
   return FALSE;
 }
 return TRUE;
}
void CDocTemplate::CloseAllDocuments(BOOL)
{
 POSITION pos = GetFirstDocPosition();
 while (pos != NULL)
 {
  CDocument* pDoc = GetNextDoc(pos);
  pDoc->OnCloseDocument();
 }
}
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章