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

1、文档类CDocument

  在"文档/视图"架构的MFC程序中,文档是一个CDocument派生对象,它负责存储应用程序的数据,并把这些信息提供给应用程序的其余部分。CDocument类对文档的建立及归档提供支持并提供了应用程序用于控制其数据的接口,类CDocument的声明如下:

/
// class CDocument is the main document data abstraction
class CDocument : public CCmdTarget
{DECLARE_DYNAMIC(CDocument) 
public:// ConstructorsCDocument();// Attributes
public:const CString& GetTitle() const;virtual void SetTitle(LPCTSTR lpszTitle);const CString& GetPathName() const;virtual void SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU = TRUE);CDocTemplate* GetDocTemplate() const;virtual BOOL IsModified();virtual void SetModifiedFlag(BOOL bModified = TRUE);// Operationsvoid AddView(CView* pView);void RemoveView(CView* pView);virtual POSITION GetFirstViewPosition() const;virtual CView* GetNextView(POSITION& rPosition) const;// Update Views (simple update - DAG only)void UpdateAllViews(CView* pSender, LPARAM lHint = 0L,CObject* pHint = NULL);// Overridables// Special notificationsvirtual void OnChangedViewList(); // after Add or Remove viewvirtual void DeleteContents(); // delete doc items etc// File helpersvirtual BOOL OnNewDocument();virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);virtual void OnCloseDocument();virtual void ReportSaveLoadException(LPCTSTR lpszPathName,CException* e, BOOL bSaving, UINT nIDPDefault);virtual CFile* GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,CFileException* pError);virtual void ReleaseFile(CFile* pFile, BOOL bAbort);// advanced overridables, closing down frame/doc, etc.virtual BOOL CanCloseFrame(CFrameWnd* pFrame);virtual BOOL SaveModified(); // return TRUE if ok to continuevirtual void PreCloseFrame(CFrameWnd* pFrame);// Implementation
protected:// default implementationCString m_strTitle;CString m_strPathName;CDocTemplate* m_pDocTemplate;CPtrList m_viewList; // list of viewsBOOL m_bModified; // changed since last savedpublic:BOOL m_bAutoDelete; // TRUE => delete document when no more viewsBOOL m_bEmbedded; // TRUE => document is being created by OLE#ifdef _DEBUGvirtual void Dump(CDumpContext&) const;virtual void AssertValid() const;#endif //_DEBUGvirtual ~CDocument();// implementation helpersvirtual BOOL DoSave(LPCTSTR lpszPathName, BOOL bReplace = TRUE);virtual BOOL DoFileSave();virtual void UpdateFrameCounts();void DisconnectViews();void SendInitialUpdate();// overridables for implementationvirtual HMENU GetDefaultMenu(); // get menu depending on statevirtual HACCEL GetDefaultAccelerator();virtual void OnIdle();virtual void OnFinalRelease();virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo);friend class CDocTemplate;protected:// file menu commands//{{AFX_MSG(CDocument)afx_msg void OnFileClose();afx_msg void OnFileSave();afx_msg void OnFileSaveAs();//}}AFX_MSG// mail enablingafx_msg void OnFileSendMail();afx_msg void OnUpdateFileSendMail(CCmdUI* pCmdUI);DECLARE_MESSAGE_MAP()
};

一个文档可以有多个视图,每一个文档都维护一个与之相关视图的链表(CptrList类型的 m_viewList实例)。CDocument::AddView将一个视图连接到文档上,并将视图的文档指针指向该文档:

void CDocument::AddView(CView* pView)
{ASSERT_VALID(pView);ASSERT(pView->m_pDocument == NULL); // must not be already attachedASSERT(m_viewList.Find(pView, NULL) == NULL); // must not be in listm_viewList.AddTail(pView);ASSERT(pView->m_pDocument == NULL); // must be un-attachedpView->m_pDocument = this;OnChangedViewList(); // must be the last thing done to the document
}

CDocument::RemoveView则完成与CDocument::AddView相反的工作:

void CDocument::RemoveView(CView* pView)
{ASSERT_VALID(pView);ASSERT(pView->m_pDocument == this); // must be attached to usm_viewList.RemoveAt(m_viewList.Find(pView));pView->m_pDocument = NULL;OnChangedViewList(); // must be the last thing done to the document
}

从CDocument::AddView和CDocument::RemoveView函数可以看出,在与文档关联的视图被移走或新加入时CDocument::OnChangedViewList将被调用:

void CDocument::OnChangedViewList()
{// if no more views on the document, delete ourself// not called if directly closing the document or terminating the appif (m_viewList.IsEmpty() && m_bAutoDelete){OnCloseDocument();return;}// update the frame counts as neededUpdateFrameCounts();
}

CDocument::DisconnectViews将所有的视图都与文档"失连":

void CDocument::DisconnectViews()
{while (!m_viewList.IsEmpty()){CView* pView = (CView*)m_viewList.RemoveHead();ASSERT_VALID(pView);ASSERT_KINDOF(CView, pView);pView->m_pDocument = NULL;}
}

实际上,类CDocument对视图的管理与类CDocManager对文档模板的管理及CDocTemplate对文档的管理非常类似,少不了的,类CDocument中可遍历对应的视图(出现GetFirstXXX和GetNextXXX两个函数):

POSITION CDocument::GetFirstViewPosition() const
{return m_viewList.GetHeadPosition();
}CView* CDocument::GetNextView(POSITION& rPosition) const
{ASSERT(rPosition != BEFORE_START_POSITION);// use CDocument::GetFirstViewPosition instead !if (rPosition == NULL)return NULL; // nothing leftCView* pView = (CView*)m_viewList.GetNext(rPosition);ASSERT_KINDOF(CView, pView);return pView;
}

CDocument::GetFile和CDocument::ReleaseFile函数完成对参数lpszFileName指定文档的打开与关闭操作:

CFile* CDocument::GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError)
{CMirrorFile* pFile = new CMirrorFile;ASSERT(pFile != NULL);if (!pFile->Open(lpszFileName, nOpenFlags, pError)){delete pFile;pFile = NULL;}return pFile;
}void CDocument::ReleaseFile(CFile* pFile, BOOL bAbort)
{ASSERT_KINDOF(CFile, pFile);if (bAbort)pFile->Abort(); // will not throw an exceptionelsepFile->Close();delete pFile;
}

CDocument类的OnNewDocument、OnOpenDocument、OnSaveDocument及OnCloseDocument这一组成员函数用于创建、打开、保存或关闭一个文档。在这一组函数中,上面的CDocument::GetFile和CDocument::ReleaseFile两个函数得以调用:

BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
{if (IsModified())TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");CFileException fe;CFile* pFile = GetFile(lpszPathName,CFile::modeRead|CFile::shareDenyWrite, &fe);if (pFile == NULL){ReportSaveLoadException(lpszPathName, &fe,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);return FALSE;}DeleteContents();SetModifiedFlag(); // dirty during de-serializeCArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);loadArchive.m_pDocument = this;loadArchive.m_bForceFlat = FALSE;TRY{CWaitCursor wait;if (pFile->GetLength() != 0)Serialize(loadArchive); // load meloadArchive.Close();ReleaseFile(pFile, FALSE);}CATCH_ALL(e){ReleaseFile(pFile, TRUE);DeleteContents(); // remove failed contentsTRY{ReportSaveLoadException(lpszPathName, e,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);}END_TRYDELETE_EXCEPTION(e);return FALSE;}END_CATCH_ALLSetModifiedFlag(FALSE); // start off with unmodifiedreturn TRUE;
}
打开文档的函数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 mesaveArchive.Close();ReleaseFile(pFile, FALSE);}CATCH_ALL(e){ReleaseFile(pFile, TRUE);TRY{ReportSaveLoadException(lpszPathName, e,TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);}END_TRYDELETE_EXCEPTION(e);return FALSE;}END_CATCH_ALLSetModifiedFlag(FALSE); // back to unmodifiedreturn 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 usBOOL bAutoDelete = m_bAutoDelete;m_bAutoDelete = FALSE; // don't destroy document while closing viewswhile (!m_viewList.IsEmpty()){// get frame attached to the viewCView* pView = (CView*)m_viewList.GetHead();ASSERT_VALID(pView);CFrameWnd* pFrame = pView->GetParentFrame();ASSERT_VALID(pFrame);// and close itPreCloseFrame(pFrame);pFrame->DestroyWindow();// will destroy the view as well}m_bAutoDelete = bAutoDelete;// clean up contents of document before destroying the document itselfDeleteContents();// delete the document if necessaryif (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 herear << var1 << var2;}else{// TODO: add loading code herevar2 >> 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 themPOSITION 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;elsereturn ;// 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行为的,因为它和其他对象的创建交错纵横。
  相信,随着我们进一步阅读后续连载,会对上述过程有更清晰的认识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432419.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos 6 安装mysql,CentOS6.5安装MySQL教程(完整教程)

Step 1&#xff1a;检测系统是否安装MYSQL# yum list installed | grep mysql顺便提下如果yum有如下提示不能用的情况&#xff1a;yum在自动更新原因是yum在自动更新 只要关掉它就OK了解决方案&#xff1a;直接输入# rm -f /var/run/yum.pid或者&#xff1a;# /etc/init.d/y…

linux学习之一

忙啦几天&#xff0c;今天终于又有时间在这里打理本园子啦&#xff0c;今天写一下linux下磁盘如何自动挂载其实很简单&#xff0c;要让某些 partition 在开机的时候就自动挂载&#xff0c;写入 /etc/fstab 当中&#xff0c;或者是将指令完整的写到 /etc/rc.d/rc.local&#xff…

MFC 多文档源码分析1

添加模板在复写的CWinApp::InitInstance()函数中添加下面代码 CMultiDocTemplate* pDocTemplate; pDocTemplate new CMultiDocTemplate(IDR_SMARTTTYPE,RUNTIME_CLASS(CCosiWorksDoc),RUNTIME_CLASS(CChildFrame), // custom MDI child frameRUNTIME_CLASS(CCosiWorksView)); …

matlab求零空间,matlab求矩阵的零空间的一组整数基,该怎样操作?

匿名用户1级2015-09-18 回答第一部分&#xff1a;矩阵基本知识一、矩阵的创建直接输入法利用Matlab函数创建矩阵利用文件创建矩阵二、矩阵的拆分矩阵元素矩阵拆分特殊矩阵三、矩阵的运算算术运算关系运算逻辑运算四、矩阵分析对角阵三角阵矩阵的转置与旋转矩阵的翻转矩阵的逆与…

php通过条件来定义const,php用const出错是什么原因

大家都知道define是定义常量的,如果在类中定义常量呢&#xff1f;当然不能用define&#xff0c;而用const&#xff0c;如下例&#xff1a;<?php //在类外面通常这样定义常量define("PHP","phpernote.com");class MyClass{ //常量的值将始终保持不变。在…

UE4角色Location远距离时动画抖动问题(float精度不够)解决方案

正题&#xff1a;关于UE4引擎当角色Location超过9999.999后&#xff0c;角色动画更新抖动问题的解决思路。 前提&#xff1a; 1.UE4引擎中距离单位是厘米(cm)&#xff0c;也就说我们制作好1.8米的角色在UE4中为180个虚幻单位。这样做个人愚见是为了提高浮点值&#xff08;float…

android Handler的使用(一)

Handler的使用(一) Handler基本概念&#xff1a; Handler主要用于异步消息的处理&#xff1a;当发出一个消息之后&#xff0c;首先进入一个消息队列&#xff0c;发送消息的函数即刻返回&#xff0c;而另外一个部分逐个的在消息队列中将消息取出&#xff0c;然后对消息进行出来…

php和python的多线程,Python多线程以及线程锁简单理解(代码)

本篇文章给大家带来的内容是关于Python多线程以及线程锁简单理解(代码)&#xff0c;有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对你有所帮助。多线程threading 模块创建线程创建自己的线程类线程通信线程同步互斥方法线程锁需要了解&#xff01;&a…

现实地形导入UE4全流程

制作地形方法很多&#xff0c;今天给大家分享一种原创野套路。此方法特点是将现实中的地形于UE4中呈现&#xff0c;而不是手动绘制地形。首先从地理空间数据云获得指定区域的地理数据&#xff0c;然后使用GlobalMapper更准确选出区域并把数据转换成WorldMachine可识别的格式&am…

php对象存储hadoop存储,三个理由告诉你对象存储替换HDFS还不错

Hadoop使企业能够对庞大的非结构化数据集进行大规模分析处理。这个数据集可以包含数以百万计&#xff0c;甚至数十亿个需要读取的文件。为了降低成本并提高数据处理性能&#xff0c;数据和应用程序应该存在于相同的物理硬件上。这样做使数据无需移动&#xff0c;就地处理&#…

Web请求中同步与异步的区别

普通的B/S模式就是同步&#xff0c;而AJAX技术就是异步&#xff0c;当然XMLHttpReques有同步的选项。 同步&#xff1a;提交请求->等待服务器处理->处理完毕返回。这个期间客户端浏览器不能干任何事。 异步: 请求通过事件触发->服务器处理&#xff08;这是浏览器仍然可…

大地形pawn抖动问题

在pawn的event tick里调用下面函数即可

php蓝牙连接不上,蓝牙音响连接不上手机怎么办 两种方法轻松解决连接问题

我们听歌一般用耳机和音响。现在蓝牙音频越来越普及&#xff0c;但毕竟是无线产品。信号不时中断或不连接是正常的。蓝牙音频连不上手机真的很头疼。出现这种情况的原因是什么&#xff0c;如何解决&#xff1f;为什么蓝牙音频不能连接到手机1.蓝牙音频没有进入匹配状态&#xf…

Bootstrap页面布局16 - BS导航菜单和其响应式布局以及导航中的下拉菜单

代码&#xff1a; <div classcontainer-fluid><h2 classpage-header>导航</h2><!--    .navrbar navbar-fixed-top:导航固定显示在顶部&#xff0c;对应的navbar-fixed-bottom:导航固定显示在页面底部    .brand:提示文字或者主题    .active…

HMI使用自定义控件流程

1.定义控件ID号&#xff0c;以FO_COMP_CUSTOM为基数#define SCENEENTITY_SIMULATOR_SHAPEFO_COMP_CUSTOM 2102.编写控件类&#xff0c;并继承于CFOBitmapShape 具体实现可以在程序中找例子&#xff0c;重新OnDraw3d这个虚函数来实现控件的绘制3. 在MainFrm.cpp中&#xff0c;在…

nginx 怎么重新编译安装mysql,centos 下 编译安装 nginx + mysql + php 服务

centos 下编译安装nginx mysql php 服务1、安装nginx1.1、安装依赖包yum install wget make gcc gcc-c pcre-devel openssl-devel -y yum install ncurses-devel libtool zilib-devel -y1.2、创建www用户useradd www -s /sbin/nologin -M1.3、创建目录mkdir -p /var/log/ngin…

hdu 1874(Dijkstra + Floyd)

链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid1874 畅通工程续 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 27692 Accepted Submission(s): 10019 Problem Description某省自从实行了很多年…

php原生session,利用Memcached在php下实现session机制 替换PHP的原生session支持

方法文件session实现文件:memcachedsession.php实现原理(也是PHP内部session的实现原理)&#xff1a;1.先判断客户端有没有sessionid&#xff0c;a.没有就添加一个sessionid给客户端&#xff0c;通常是32位hash码&#xff0c;同时初始化一个数组做session容器b.如果客户端有ses…

Web 开发中很实用的10个效果【附源码下载】

在工作中&#xff0c;我们可能会用到各种交互效果。而这些效果在平常翻看文章的时候碰到很多&#xff0c;但是一时半会又想不起来在哪&#xff0c;所以养成知识整理的习惯是很有必要的。这篇文章给大家推荐10个在 Web 开发中很有用的效果&#xff0c;记得收藏&#xff01; 超炫…

深入分析MFC文档视图结构(项目实践)

文档视图结构&#xff08;Document/View Architecture&#xff09;是MFC的精髓&#xff0c;也是Observer模式的具体实现框架之一&#xff0c;Document/View Architecture通过将数据和其表示分开&#xff0c;提供了很好的数据层次和表现层次的解耦。然而&#xff0c;虽然我们使用…