现在的位置: 首页 > 综合 > 正文

深入浅出MFC文档/视图架构之文档(2)

2014年01月27日 ⁄ 综合 ⁄ 共 4563字 ⁄ 字号 评论关闭
打开文档的函数CDocument::OnOpenDocument完成的工作包括如下几步:

  (1)打开文件对象;

  (2)调用DeleteDontents();

  (3)建立与此文件对象相关联的CArchive对象;

  (4)调用应用程序文档对象的Serialize()函数;

  (5)关闭CArchive对象、文件对象。

BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)
{
 CFileException fe;
 CFile* pFile = NULL;
 pFile = GetFile(lpszPathName, CFile::modeCreate |CFile::modeReadWrite | CFile::shareExclusive, &fe);

 if (pFile == NULL)
 {
  ReportSaveLoadException(lpszPathName, &fe,TRUE, AFX_IDP_INVALID_FILENAME);
  return FALSE;
 }

 CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete);
 saveArchive.m_pDocument = this;
 saveArchive.m_bForceFlat = FALSE;
 TRY
 {
  CWaitCursor wait;
  Serialize(saveArchive); // save me
  saveArchive.Close();
  ReleaseFile(pFile, FALSE);
 }
 CATCH_ALL(e)
 {
  ReleaseFile(pFile, TRUE);

  TRY
  {
   ReportSaveLoadException(lpszPathName, e,TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
  }
  END_TRY
  DELETE_EXCEPTION(e);
  return FALSE;
 }
 END_CATCH_ALL
 
 SetModifiedFlag(FALSE); // back to unmodified
 return TRUE; // success
}

  保存文档的函数CDocument::OnSaveDocument完成的工作包括如下几步:

  (1)创建或打开文件对象;

  (2)建立相对应的CArchive对象;

  (3)调用应用程序文档对象的序列化函数Serialize();

  (4)关闭文件对象、CArchive对象;

  (5)设置文件未修改标志。

void CDocument::OnCloseDocument()
// must close all views now (no prompting) - usually destroys this
{
 // destroy all frames viewing this document
 // the last destroy may destroy us
 BOOL bAutoDelete = m_bAutoDelete;
 m_bAutoDelete = FALSE; // don't destroy document while closing views
 while (!m_viewList.IsEmpty())
 {
  // get frame attached to the view
  CView* pView = (CView*)m_viewList.GetHead();
  ASSERT_VALID(pView);
  CFrameWnd* pFrame = pView->GetParentFrame();
  ASSERT_VALID(pFrame);

  // and close it
  PreCloseFrame(pFrame);
  pFrame->DestroyWindow();
  // will destroy the view as well
 }
 m_bAutoDelete = bAutoDelete;

 // clean up contents of document before destroying the document itself
 DeleteContents();

 // delete the document if necessary
 if (m_bAutoDelete)
  delete this;
}

  CDocument::OnCloseDocument函数的程序流程为:

  (1)通过文档对象所对应的视图,得到显示该文档视图的框架窗口的指针;

  (2)关闭并销毁这些框架窗口;

  (3)判断文档对象的自动删除变量m_bAutoDelete是否为真,如果为真,则以delete this语句销毁文档对象本身。

  实际上,真正实现文档存储和读取(相对于磁盘)的函数是Serialize,这个函数通常会被CDocument的派生类重载(加入必要的代码,用以保存对象的数据成员到CArchive对象以及从CArchive对象载入对象的数据成员状态):

void CExampleDoc::Serialize(CArchive& ar)
{
 if (ar.IsStoring())
 {
  // TODO: add storing code here
  ar << var1 << var2;
 }
 else
 {
  // TODO: add loading code here
  var2 >> var1 >> ar;
 }
}

  地球人都知道,文档与视图进行通信的方式是调用文档类的UpdateAllViews函数:

void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)
// walk through all views
{
 ASSERT(pSender == NULL || !m_viewList.IsEmpty());
 // must have views if sent by one of them

 POSITION pos = GetFirstViewPosition();
 while (pos != NULL)
 {
  CView* pView = GetNextView(pos);
  ASSERT_VALID(pView);
  if (pView != pSender)
   pView->OnUpdate(pSender, lHint, pHint);
 }
}

  UpdateAllViews函数遍历视图列表,对每个视图都调用其OnUpdate函数实现视图的更新显示。

  2.文档的OPEN/NEW

  从连载2可以看出,在应用程序类CWinapp的声明中包含文件的New和Open函数:

afx_msg void OnFileNew();
afx_msg void OnFileOpen();

  而在文档模板管理者类CDocManager中也包含文件的New和Open函数:

virtual void OnFileNew();
virtual void OnFileOpen();
virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file

  而文档模板类CDocTemplate也不例外:

virtual CDocument* OpenDocumentFile(
 LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE) = 0;
 // open named file
 // if lpszPathName == NULL => create new file with this type
 virtual CDocument* CreateNewDocument();

  复杂的是,我们在CDocument类中再次看到了New和Open相关函数:

virtual BOOL OnNewDocument();
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);

  在这众多的函数中,究竟文档的创建者和打开者是谁?"文档/视图"框架程序"File"菜单上的"New"和"Open"命令究竟对应着怎样的函数调用行为?这一切都使我们陷入迷惘!

  实际上"文档/视图"框架程序新文档及其关联视图和框架窗口的创建是应用程序对象、文档模板、新创建的文档和新创建的框架窗口相互合作的结果。具体而言,应用程序对象创建了文档模板;文档模板则创建了文档及框架窗口;框架窗口创建了视图。

  在用户按下ID_FILE_OPEN及ID_FILE_NEW菜单(或工具栏)命令后,CWinApp(派生)类的OnFileNew、OnFileOpen函数首先被执行,其进行的行为是选择合适的文档模板,如图3.1所示。


图3.1文档模板的选择

  实际上,图3.1中所示的"使用文件扩展名选择文档模板"、"是一个文档模板吗?"的行为都要借助于CDocManager类的相关函数,因为只有CDocManager类才维护了文档模板的列表。CDocManager::OnFileNew的行为可描述为:

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);
}

  之后,文档模板类的virtual CDocument* OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE) = 0函数进行文档的创建工作,如果lpszPathName == NULL,是文档New行为;相反,则是Open行为。在创建框架后,文档模板根据是Open还是New行为分别调用CDocument的OnOpenDocument、OnNewDocument函数。图3.2描述了整个过程。


图3.2文档、框架窗口的创建顺序

  而图3.3则给出了视图的创建过程。


图3.3视图的创建顺序

  图3.1~3.3既描述了文档/视图框架对ID_FILE_OPEN及ID_FILE_NEW命令的响应过程,又描述了文档、框架窗口及视图的创建。的确,是无法单独描述文档的New和Open行为的,因为它和其他对象的创建交错纵横。

  相信,随着我们进一步阅读后续连载,会对上述过程有更清晰的认识。  

抱歉!评论已关闭.