第8章Document-View 深入探讨
代码的位置在点击这下载:http://download.csdn.net/detail/caidaoqq/6951265
1. CDocTemplate管理CDocument / CView / CframeWnd
图8-2 解释CDocTemplate、CDocument、CView、CFrameWnd 之间的关系。下面则是
一份文字整理:
■ CWinApp 拥有一个对象指针:CDocManager* m_pDocManager。
■ CDocManager 拥有一个指针串行CPtrList m_templateList,用来维护一系列的
Document Template。一个程序若支持两「种」文件类型,就应该有两份Document
Templates,应用程序应该在CMyWinApp::InitInstance 中以AddDocTemplate 将
这些Document Templates 加入由CDocManager 所维护的串行之中。
■ CDocTemplate 拥有三个成员变量,分别持有Document 、View、Frame 的
CRumtimeClass 指针,另有一个成员变量m_nIDResource,用来表示此Document
显现时应该采用的UI 对象。这四份资料应该在CMyWinApp::InitInstance 函数
构造CDocTemplate(注1)时指定之,成为构造式的参数。当使用者欲打开一
份文件(通常是借着【File/Open】或【File/New】命令项),CDocTemplate即
可藉由Document/View/Frame 之CRuntimeClass 指针(注2)进行动态生成。
■ CDocument 有一个成员变量CDocTemplate* m_pDocTemplate,回指其Document
Template;另有一个成员变量CPtrList m_viewList,表示它可以同时维护一系列
的Views。
■ CFrameWnd 有一个成员变量CView* m_pViewActive ,指向目前正作用中的
View 。
■ CView 有一个成员变量CDocument* m_pDocument,指向相关的Document。
2. Scribble Step1 的Document---数据结构设计
MFC Collection Classes。它们分为三种类型,用来管理一大群对象:
■ Array:数组,有次序性(需依序处理),可动态增减大小,索引值为整数。
■ List:双向串行,有次序性(需依序处理),无索引。串行有头尾,可从头尾
或从串行的任何位置安插元素,速度极快。
■ Map:又称为Dictionary,其内对象成对存在,一为键值对象(key object),
一为实值对象(value object)。
CPenPaintTestDoc的成员变量:
■ m_strokeList:这是一个CObList 对象,代表一个串行。串行中的元素是什么型
态?答案是CObject*。但实际运作时,我们可以把基础类别之指针指向衍生类
别之对象。现在我们想让这个串行成为「由CStroke对象构成的串行」,因此显然CStroke
必须衍生自
CObject 才行,而事实上它的确是。
■ m_nPenWidth:每一线条都有自己的笔宽,而目前使用的笔宽记录于此。
■ m_penCur:这是一个CPen 对象。程序依据上述的笔宽,配置一支笔,准备用
来画线条。笔宽可以指定。注意,笔宽的设定对象是线条,不是单一的点,也不是一整张图。
CPenPaintTestDoc的成员函数:
■ OnNewDocument、OnOpenDocument、InitDocument。产生Document 的时机有二,
一是使用者选按【File/New】,一是使用者选按【File/Open】。当这两种情况
发生,Application Framework会分别调用Document 类别的OnNewDocument
OnOpenDocument。
■ NewStroke。这个函数将产生一个新的CStroke对象,并把它加到串行之中
结果:产生了一个新线条,设定了线条宽度,并将新线条加入串行尾端
■ DeleteContent。利用CObList::RemoveHead把串行的最前端元素拿掉。
■ Serialize。这个函数负责文件读写。由于文件掌管线条串行,线条串行又掌管
各线条
CStroke的成员变量
■ m_pointArray:这是一个CArray 对象,用以记录一系列的CPoint 对象,这些
CPoint 对象由鼠标坐标转化而来。
■ m_nPenWidth:一个整数,代表线条宽度。虽然ScribbleStep1 的线条宽度是
固定的。
CStroke的成员函数
■ DrawStroke :绘图原本是View 的责任,为什么却在CStroke 中有一个
DrawStroke?因为线条的内容只有CStroke 自己知道,当然由CStroke 的成
员函数把它画出来最是理想。这么一来,View 就可以一一调用线条自己的绘
图函数,很轻松。
■ Serialize:让我们这么想象写档动作:使用者下命令给程序,程序发命令给文
件,文件发命令给线条,线条发命令给点数组,点数组于是把一个个的坐标点
写入磁盘中。
3. Scribble Step1 的View:资料重绘与编辑
View 有两个最重要的任务,一是负责资料的显示,另一是负责资料的编辑
本例的CPenPaintTestView包括以下特质:
■„解读CPenPaintTestDoc中的资料,包括笔宽以及一系列的CPoint 对象,画在View
窗口上。
■ 允许使用者以鼠标左键充当画笔在View 窗口内涂抹,换句话说CPenPaintTestView
必须接受并处理WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP
三个消息。
View 的重绘动作:GetDocument 和OnDraw
以下是CPenPaintTestView中与重绘动作有关的成员变量和成员函数。
CPenPaintTestView的成员变量
■ m_pStrokeCur:一个指针,指向目前正在工作的线条。
■ m_ptPrev:线条中的前一个工作点。我们将在这个点与目前鼠标按下的点之间
画一条直线。虽说理想情况下鼠标轨迹的每一个点都应该被记录下来,但如果
鼠标移动太快来不及记录,只好在两点之间拉直线。
CPenPaintTestView的成员函数
■ OnDraw:这是一个虚拟函数,负责将Document的数据显示出来。改写它是程
式员最大的责任之一。
■ GetDocument:AppWizard为我们做出这样的码
■ OnPreparePrinting,OnBeginPrinting,OnEndPrinting:这三个CView 虚拟函数将
用来改善打印行为。
Serialize:对象的文件读写
对象必须能够永续生存,也就是它们必须能够在程序结束时储存到文件中,并且在程序重新激活时再恢复回来。储存和恢复对象的过程在MFC 之中就称为serialization。负责这件重要任务的,是MFC CObject 类别中一个名为Serialize的虚拟函数,文件的「读」「写」动作均透过它。
Scribble程序的文件读写动作是这么分工的:
■ Framework 调用CPenPaintTestDoc::Serialize,用以对付文件。
■ CPenPaintTestDoc再往下调用CStroke::Serialize,用以对付线条。
■ CStroke 再往下调用CArray::Serialize,用以对付点数组。
Serializable的必要条件:
1. 从CObject 衍生下来。如此一来可保有RTTI、Dynamic Creation 等机能。
2. 类别的声明部份必须有DECLARE_SERIAL 宏。此宏需要一个参数:类别
名称。
3. 类别的实作部份必须有IMPLEMENT_SERIAL 宏。此宏需要三个参数:
一是类别名称,二是父类别名称,三是schemano.。
4. 改写Serialize 虚拟函数,使它能够适当地把类别的成员变量写入文件中。
5. 为此类别加上一个default 构造式(也就是无参数之构造式)。这个条件常为
人所忽略,但它是必要的,因为若一个对象来自文件,MFC必须先动态生成
它,而且在没有任何参数的情况下调用其构造式,然后才从文件中读出对象资
料。