深入浅出MFC学习笔记(第8章:Document----View深入探讨)

深入浅出MFC学习笔记 专栏收录该内容
7 篇文章 1 订阅

                      第八章:Document----View深入探讨

    MFC之所以为应用程序框架,最重要的特征就是他能够将管理数据的程序代码和负责数据显示的程序代码分离开来。这种能力由文档视图结构提供。DocumentMFCCDocument中被实例化,它本身就是一个空壳,当开发自己的程序时,应该从CDocument派生出一个属于自己的CMyDocument类,并且在类中声明一些成员变量,用以容纳数据。然后再改写Serialize函数,CDocument派生自CObject类,所以它具有CObject所支持的一切性质,包括运行时类型识别,动态创建,文件读写,又由于它派生自CCmdTarget,所以它可以接受来自菜单或工具栏的WM_COMMAND消息。

    CView负责数据的显示。CView本身并无用途,它也是提供了一个空壳,当开发程序时,应该从CView派生出一个属于自己的CMyView类,并且在类中改写负责数据显示的OnDraw函数。由于CView派生自CWnd,因此它可以接受一般的Windows消息,又由于它派生自CCmdTarget,所以它可以接收来自菜单或工具栏的WM_COMMAND消息。

    View是一个没有边框的窗口,其外围还有一个Frame窗口。

    CDocTemplate负责管理DocumentViewFrame。它有两个派生类,分别为:CMultiDocTemplateCSingleDocTemplate。如果程序能够处理两种以上的数据,就必须构造两个Document Template出来,然后调用CWinApp::AddDocTemplate将它们加入到系统之中。这和程序是不是MDI没有关系。如果程序支持多种数据类型,但是是SDI,那只是每次只能打开一个文件罢了,MDI可以同时打开多个。

    CDocTemplate用以管理ViewdocumentFrame。而CWinApp管理CDocTemplate。在CWinApp::InitInstance中构建CDocTemplate

Bool CMyWinApp::InitInstance()

{

    .....

    CMultiDocTemplate *pDocTemplate=new CMultiDocTemplate(

            IDR_SCRIBYTEP,

            RUNTIME_CLASS(CMyDoc),

            RUNTIME_CLASS(CChildFrame),

            RUNTIME_CLASS(CMyView);

      AddDocTemplate(pDocTemplate);

}

    注意构造CView对象和产生View窗口之间的区别。CView类用于管理View窗口,它内部含有要产生的View窗口的句柄,因此必须首先构造CView对象,在调用该类的成员函数如Create函数产生窗口。

    由于在构建CDocTemplate时,传递的是documentViewFrameCRuntimeClass结构,在CDocTemplate内部,会构建调用各自类的构建函数构建这三个对象。这是在CMultiDocTemplate::OpenDocumentFile内实现的。

    当使用者单击File/NewCWinApp::OnFileNew函数被调用,然后它又调用CDocManager::OnFileNew,后者又调用CMultiDocTemplate::OpenDocumentFile.此函数调用CreateNewDocument动态创建Document,调用CreateNewFrame动态产生Document Frame。然而View的产生就没有这么显而易见了,首先Frame的创建发出WM_CREATE消息,是CFrameWnd::OnCreate被调用,经过一些列调用,在最后调用的CFrameWnd::CreateView内,View对象以及windows窗口都被创建出来。

    CWinApp又有一个指向CDocManager的指针,它指向CDoManager类型的对象。在CDocManager类中,有一个指针链表,用来维护一系列的Document Template,如果一个程序支持两种以上的文件类型,那么就应该调用AddDocTemplateDocument Template加入到链表中。

    CDocTemplate拥有三个成员变量,分别为指向document ,viewFrameCRuntimeClass指针,另一个成员为m_nIDResource。用来表示此Document显现时应该使用的UI对象。这四个成员应该在

CMyWinApp::InitInstance中构造CDocTemplate时指定。然后CDocTemplate可以借助这些参数,动态构建DocumentviewFrame。如果是单文档程序,Document Template就是CSingleDocTemplate,此时它有一个m_pOnlyDoc成员变量,也就是说它只能打开一份文档。如果为多文档应用程序,Document Template使用的就是CMultiDocTemplate,它有一个成员变量m_docList,此变量为CPtrList类型,指向一系列打开的文档。

    CDocument有一个成员变量CDocTemplate,指向它所属的Document Template。另一个成员变量m_viewList指向它维护的一系列view

   CFrameWnd有一个成员m_pViewActive指向当前活动的View

CView有一个成员变量,m_pDocument指向相关的Document

    类似于C++STL中的顺序容器vectorListMFC也提供了自己的容器类。它们可以分为ListArray Map;

   CArray ,CList ,CMap为模板类,使用时需要指定元素类型和函数的参数类型。

如 CArray<TYPE,ARG_TYPE> a;

   CList <TYPE ,ARG_TYPE> b;

   CMap<KEY,ARG_KEY,VALUE,ARG_VALUE> c;

   ARG_TYPE则用来指定函数的参数类型。

类型指针使用时仍然需要指定参数:

CTypedPtrArray<BASE_CLASS,TYPE> a;

CTypedPtrList<BASE_CLASS,TYPE>b;

CTypedMap<BASE_CLASS,KEY,VLAUE>c;

   TYPE用以指定元素的类型。

   BASE_CLASS则用来指定基类的类型。它可以是任何非模板的容器。如CObList0.CPtrList,CPtrArray

MFC容器类支持的对象中,有两种需要特别说明:

   一是Ob,表示派生自CObject的任何对象。MFC提供CObListCObArray两种类。

   二是Ptr,表示对象指针。MFC提供CPtrList,CPtrArray两种类。 

   绝大部分的MFC类,以及自己写的类都要从CObject派生下来,因为当一个类派生自CObject时,它也就继承了许多重要的性质,它至少提供两个机能:IsKindOfIsSerialzable.

IsKindOf它是判断一个对象是不是某个类。它是依赖于CRuntimeClass

bool CObject::IsKindOf(const CRuntimeClass *pClass)const 

{

   CRuntimeClass *pClassThis=GetRuntimeClass();

   return pClassThis->IsDerivedFrom(pClass);

}

bool CRuntimeClass::IsDerived(cosnt CRuntimeClass*pBaseClass)const

{

   const CRuntimeClass *pClassThis=this;

   whiel(pClassThis)

   {

     if(pClassThis==pBaseClass)

        return true;

    pClassThis=pClassThis->pBaseClass;

   }

  return false;

}

IsSerializable函数用以判断一个类是否可以Serialize,这主要通过判断CRuntimeClassschema成员是否为0xFFFF.

bool CObject ::IsSerialize()const

{

  return (GetRuntimeClass()->m_wSchema!=0xffff);

}

    CObject有一个虚函数Serialize,每一个希望具备serialize功能的类都需要重写这个函数。

    DYNCREATE/DYNCREATE/SERIAL分别在CRuntimeClass所组成的类型型录中填写不同的记录。它们分别对应三个不同等级。

    DYNAMIC 仅具备动态类型识别。

    DYNCREATE包括DYNAMIC同时还具备动态创建。

    SERIAL包括DYNCREATE,还包括Serialize.

    CArchive类管理文件缓冲区。它是Serialize的对象。CArchive针对许多C++数据类型,windows数据类型以及CObject派生类定义了operator<<operator>>重载运算符。正因为如此,可以将各种类型的数据读出或写入CArchive。一个自定义的C++类如果想要有Serialization机制,就得直接或间接派生自CObject。为的是从CObject派生下列三个运算符:

_AFX_INLINE CArchive &AFXAPI operator<<(CArchive&ar,const CObject*pOb);

_AFX_INLINE CArchive &AFXAPI operator>>(CArchive&ar,CObject*&2pOb);

_AFX_INLINE CArchive &AFXAPI operator>>(CArchive&ar,const CObject*&pOb);

一个类如果希望有Serialization机制,它的第二要件就是使用SERIAL宏。

这个宏包含DYNCRETE宏,比DYNCREATE多了个:

friend CArchive &AFXAPI operator>>

                           (CArchive&ar,class_name*&pOb);

在定义处增添了:

CArchive &AFXAPI operator>>(CArchive&ar,class_name*&pOb)/

 {/

  pOb=(class_name*)ar.ReadObject(RUNTIME_CLASS(class_name));/

return ar;/

}

    此处只改写了CObject::operator>>而没有改写operator<<,原因是WriteObject并不需要CRuntimeClass信息。但ReadObject需要,因为在读完文件之后还要进行动态创建操作。

    前面就提到过CObject会抽象类且它没有基类,因此它需要特殊处理。

    当多个视图显示同一个文档,为了保持各个视图操作的文档内容的一致性,需要以消息通知使用同一份文档的其他视图,CView中有三个虚函数:

    1CView::OnInitialUpdate:负责View的初始化。

    2CView::OnUpdate,当FrameWork调用此函数时,表示Document的内容已经发生了变化。

    3CView::OnDraw:WM_PAINT消息时会调用此函数,此函数负责更新View窗口的内容。

让所有的View窗口同步更新数据的关键在于两个函数:

    1CDocument::UpdateAllViews,它会遍历使用这个文档的各个视图,逐个调用它们的OnUpdate函数。

    2CView::OnUpdate,这是个虚函数,可以改写。它的作用就是告诉Viewdocument的内容已经改变,你需要更新了。

具体步骤为:
    1:CView中调用GetDocument获得CDocument指针。

    2:CView中调用CDocument::OnUpdateAllViews;

    3:所有使用这一份Documentview都被调用OnUpdate

  • 5
    点赞
  • 1
    评论
  • 4
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

文档视图结构(Document/View Architecture)是MFC的精髓,也是Observer模式的具体实现框架之一, Document/View Architecture通过将数据和其表示分开,提供了很好的数据层次和表现层次的解耦。然而, 虽然我们使用MFC AppWizard就可以很轻松地获得一个支持Document/View Architecture的MFC程序框架, Document/View Architecture本身的复杂性加上VC产生的一系列代码足够让我们眼花缭乱,一时陷入云里雾里不可自拔。 加上我们更多的人都没有经过Windows SDK Programming 的学习和经历就直接进行MFC Programming的学习和开发, 更是面对一堆的代码不知所措。 之于Document/View Architecture,侯捷先生的《深入浅出MFC》一书确实进行了很深入的分析和研究,网络上也有很多在侯捷先生著述 的基础上的进一步的文出现,但是个人觉得这里面有一点瑕疵(仅代表k_eckel浅见):太过深入, 这些分析和研究都最终会定位到Windows SDK中窗口的创建过程、MFC中对Document/View Architecture支持所提供的复杂无序的宏等 对于没有Windows SDK Programming 经验和经历的学习者和对于MFC不是很熟悉的学习者无异于是徒增烦恼,一个本来就很复杂的问题 更加地复杂化了。我的观点是这个过程是必要的,然而不是每个人所必需的,或者说不是每个人在初期学习和绝大多数项目开发中所 必需的。我向来对众多的仅仅学会了拖拉点拽就以为会了MFC(侯捷先生在《深入浅出MFC》一书中对这个群体有一个照面,这里不罗嗦) 不以为然,但是我依然认为轻量级学习成本是重要的,容易上手,易于接受是一门技术成功或者说有价值的一个很大的决定性因素。 因此提供一个轻量级的学习过程对于学习来说是很有必要性的,本系列文就遵循这样一个理念,对MFCDocument/View Architecture 进行一个分析(姑且也可以称之为深入),尽量将对Document/View Architecture的理论研究(侯捷先生书中很多内容)和实际的项目 开发结合起来,最后提供一个简单但是全面的Document/View Architecture项目开发(主要是界面框架设计和开发)的实际例子,供参考。
评论 1 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页

打赏作者

ithzhang

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值