Download
slide1 n.
Skip this Video
Loading SlideShow in 5 Seconds..
第12章 基于文档/视图结构的程序 PowerPoint Presentation
Download Presentation
第12章 基于文档/视图结构的程序

第12章 基于文档/视图结构的程序

257 Vues Download Presentation
Télécharger la présentation

第12章 基于文档/视图结构的程序

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

  1. 第12章 基于文档/视图结构的程序 • 本章内容主要包括建立基于文档/视图结构的应用程序方法,文档/视图结构应用程序的组成类及在应用程序架构中的作用,文档类CDocument与视图CView类,建立从CObject派生的类并实现对象的序列化操作。 要求了解建立文档/视图结构应用程序的方法、应用程序向导创建的三种应用程序类型、VC中对单链表的处理。以Word为例,熟悉MDI应用程序中各个窗口所对应的类是什么,掌握文档类和视图类的常见成员函数、文档类和视图类的交互,熟练掌握VC中自定义类的持久化处理方法。

  2. 第12章 基于文档/视图结构的程序 12.1 文档/视图结构 12.2 框架——视图的容器 12.3 文档——管理和组织数据 12.4 视图——显示文档数据,提供用户接口 习题

  3. 12.1 文档/视图结构 • 文档/视图结构是在VC中使用MFC开发基于文档应用程序的基本框架,在这个框架中,数据的维护及其显示分别由两个不同的,但又彼此紧密相关的对象——文档和视图负责的。文档对象通常代表应用程序中打开的一个文件,而视图窗口则提供了该文档对象中数据的可视化表示形式并接收用户的交互。文档和视图关系是一对多的关系,即一个文档可以对应多个视图(如一个Word文档有大纲视图、页面视图、Web板式视图等),但一个视图只能对应一个文档。 12.1.1 VC开发的应用程序类型 12.1.2 文档/视图结构程序的主要组成类 12.1.3 从程序员角度看SDI与MDI应用程序 12.1.4 应用程序类 12.1.5 文档/视图结构优点

  4. 12.1.1 VC开发的应用程序类型 • 利用MFC应用程序向导可以开发三种类型的应用程序: • 第一种是基于对话框的应用程序,上一章讲的对话框与控件大部分是基于对话框的应用程序。 • 第二种是基于SDI(Single Document Interface单文档接口)的应用程序,其特点是用户一次只能打开一个文档,若已经打开一个文档,想打开另一个文档时,必须关闭当前打开的文档。 • 第三种基于MDI(Multi Document Interfac多文档接口)的应用程序,其特点是用户一次可以打开多个文档,若已经打开一个文档,又想打开另一个文档时,不必关闭当前打开的文档,它体现了Windows操作系统的多任务特点。

  5. 12.1.2 文档/视图结构程序的主要组成类 • 1.文档/视结构概述 在MFC 应用程序中,文档/视图结构用来将程序的数据本身(靠CDocument类来管理)与数据的显示以及用户对数据的交互(靠CView类来管理)相隔离,这种模式与数据库系统中的“表/视图”关系相似。 • 2.SDI应用程序的组成类单文档应用程序是一种比较简单的基于文档/视图结构的程序,程序在运行过程中,只能打开该程序所关联的(如Word子处理程序关联的的是.Doc文件,即双击某个.Doc文件,启动它的是Word程序)一个文件。 • 3.MDI应用程序的组成类多文档应用程序是一种比较复杂的基于文档/视图结构程序,程序在运行过程中,可以打开该程序所关联的(如Excel电子表格处理程序关联的的是.xls文件,即双击某个.xls文件,启动它的是Excel程序)多个文件。

  6. 【例12-1】本实例实现一个简单的文本编辑器功能。它是基于单文档的应用程序。【例12-1】本实例实现一个简单的文本编辑器功能。它是基于单文档的应用程序。 • (1)实例创建步骤 • ① 创建一个新的MFC AppWizard(exe)工程,工程名称为MyEditor。 • ② 在AppWizard的第一步中,选择类型为Single Document(单文档类型)。 • ③ AppWizard的第二步、第三步中使用缺省的选项,在第四步中,单击Advance(高级)按钮,弹出Advance Options对话框,在File Extension(文件扩展名)编辑框中输入txt2,如图12.1所示。完后按Close按钮。 • ④ AppWizard的第五步使用缺省的选项,在第六步中,将CMyEditorView类的基类设置为CEditView后,按Close按钮关闭该对话框,在余下的几步中,全部使用默认值。最后单击“Finish”按钮,生成应用程序框架。 • (2)SDI应用程序的组成类 • 利用MFC AppWizard生成一个基于单文档的应用程序后,打开WorkSpace中的ClassView选项页,可以浏览到单文档应用程序的一些核心的组成类,如图12.2所示,理解这些类及其之间的关系是理解该类应用程序的关键。

  7. 图(一) • 图12.2 SDI程序的主要类图 图12.1 文件的高级选项对话框

  8. 【例12-1】(续) • 向导生成的一个典型的单文档应用程序包括一个应用程序类CMyEditorApp、一个主框架窗口类CMainFrame、一个文档类CMyEditorDoc、一个视图类CMyEditorView、一个“关于”对话框类CAboutDlg。 • 打开上述五个类的定义文件,可以看到,基于文档/视图结构的SDI应用程序至少由以下的几个类来组成: • ①应用程序类CMyEditorApp是一个从CWinApp类派生的类,它充当全部应用程序的容器。 • ② 框架窗口类CMainFrame是一个从CFrameWnd类派生的类。在SDI应用程序中,框架窗口就是应用程序的主窗口,主窗口就是应用程序直接放置于桌面上的那个窗口,每个应用程序只能有一个主窗口,主窗口的标题栏上常常显示应用程序的名称(如在记事本程序中,新建一个文件后,主窗口的标题栏上显示“未定标题-记事本”)。 • ③ 文档类CMyEditorDoc是从CDocument类继承而来的类,它存储应用程序的数据,并将这些信息提供给应用程序的其余部分。 • ④ 视图类CMyEditorView是从CEditView类派生而来的,CEditView类提供了基本的文本编辑功能,是从CView类派生来的。视图窗口与其父窗口——框架窗口的客户区域对齐,它接收用户对应用程序的输入并显示相关联的文档数据。 • ⑤ 对话框类CAboutDlg是从CDialog派生来的,该对话框类显示一个简单的版本信息的“关于”对话框。对话框类的说明前面有一章已经专门讲解了。

  9. 【例12-1】(续) • (3) 运行SDI应用程序 • 编译并运行上述程序的显示结果如图12.3所示。在客户区中输入一些文本,然后保存。再新建一个,刚才所输入内容的文件已经关闭。这是一个典型的单文档应用程序,看它是否和记事本程序的功能类似。 • (4) 保存文档 • 在MyEditor应用程序的客户区输入一些文本后,单击“文件|保存”,则弹出图12.4所示的“文件保存”对话框,将文件名编辑框中输入一个文件名如vc6,则在制定的目录下保存了一个vc6.txt2文件,其中保存类型编辑框中出现的的文字就是在图12.1中的设置所起的作用。 • 另外,在Window下,我们直接双击刚才建立的文件vc6.txt2,则该文件自动用程序MyEditor打开,和在Windows下用鼠标双击某个.doc文件,则该文件自动用Word字处理程序打开类似,该功能是怎样实现的呢? • 定位到应用程序类的InitInstance()函数中,可以看到下面的代码:

  10. BOOL CMyEditorApp::InitInstance() • { • …… • CSingleDocTemplate* pDocTemplate; • pDocTemplate = new CSingleDocTemplate( • IDR_MAINFRAME, • RUNTIME_CLASS(CMyEditorDoc), • RUNTIME_CLASS(CMainFrame), // main SDI frame window • RUNTIME_CLASS(CMyEditorView)); • AddDocTemplate(pDocTemplate); • // Enable DDE Execute open • EnableShellOpen(); • //使得双击.txt2文件类型时能调用该工程MyEditor打开 • RegisterShellFileTypes(TRUE); • //在注册表中注册文件类型.txt2 • // Parse command line for standard shell commands, DDE, file open • CCommandLineInfo cmdInfo; • ParseCommandLine(cmdInfo); • …… • } • 上述两行加黑的代码的功能就是实现某种类型的文件与某个应用程序关联的语句,有了它就能在双击某种类型的文件时,调用相应的应用程序把其打开。

  11. 图(二) • 图12.4 文件保存对话框 图12.3 MyEditor的运行界面

  12. 【例12-2】本实例实现一个丰富文本编辑功能,即可以在当前文档中插入其它的图形、音频等。它是基于多文档的应用程序。【例12-2】本实例实现一个丰富文本编辑功能,即可以在当前文档中插入其它的图形、音频等。它是基于多文档的应用程序。 • (1)实例创建步骤 • ①创建一个新的MFC AppWizard(exe)工程,工程名称为MyWord。 • ② 在AppWizard的第一步中,选择类型为Multiple Documents(多文档类型)。 • ③AppWizard的第二步使用缺省的选项,在第三步中主要选择如何支持复合档支持 文档的,我们选择“Container”容器支持,如图12.5所示,完后按Next按钮。 • ④AppWizard的第二步、第三步中使用缺省的选项,在第四步中,单击Advance(高级)按钮,弹出Advance Options对话框,在File Extension(文件扩展名)编辑框中输入doc2,如图12.6所示。完后按Close按钮。 • ⑤AppWizard的第五步使用缺省的选项,在第六步中,将CMyWordView类的基类设置为CRichEditView后,按Close按钮关闭该对话框,在余下的几步中,全部使用默认值。最后单击“Finish”按钮,生成应用程序框架。 • (2)MDI应用程序组成类 • 利用MFC AppWizard生成一个基于多文档的应用程序后,打开WorkSpace中的ClassView选项页,可以浏览到多文档应用程序的一些核心的组成类,如图12.7所示,理解这些类及其之间的关系是理解该类应用程序的关键。

  13. 图(三) 图12.5 选择如何支持复合文档对话框 12.7 MDI应用程序中的主要类图 图12.6 文件的高级选项对话框图

  14. 【例12-2】(续) • 向导生成的一个典型的多文档应用程序包括一个应用程序类CMyWordApp、一个主框架窗口类CMainFrame、一个子框架窗口类CChildFrame、一个文档类CMyWordDoc、一个视图类CMyWordView、一个“关于”对话框类CAboutDlg。由于该程序可以是复合文档的容器,即该程序可以嵌入别的应用程序对象,故该类中还有一个丰富文本条目类CMyWordCntrItem。 • 打开上述七个类的定义文件,可以看到,基于文档/视图结构的MDI应用程序至少由以下的几个类来组成: • 应用程序类CMyWordApp是一个从CWinApp类派生的类,它充当全部应用程序的容器。 • 框架窗口类CMainFrame是一个从CMDIFrameWnd类派生的类,在MDI应用程序中,框架窗口就是应用程序的主窗口,主窗口就是应用程序直接放置于桌面上的哪个窗口,每个应用程序只能有一个主窗口,主窗口的标题栏上常常显示应用程序的名称(如在记事本程序中,新建一个文件后,主窗口的标题栏上显示“未顶定标题-记事本”)。 • 子框架窗口类CChildFrame是一个从CMDIChildWnd类派生的类,在MDI应用程序中,子框架窗口是包含在MDI主窗口的一个子窗口,该子窗口不能有自己的菜单,但可以共享MDI主窗口的菜单。该窗口主要用来容纳视图窗口。 • 文档类CMyWordDoc是从CRichEditDoc类继承而来的类,而CRichEditDoc又是从CDocument继承而来的,它存储应用程序的数据(可以包含音频和视频数据,即可存储丰富文本),并将这些信息提供给应用程序的其余部分。

  15. 【例12-2】(续) • 视图类CMyWordView是从CRichEditView类派生而来的,CRichEditView类提供了丰富的文本编辑功能(可以编辑图形图像数据,即可编辑丰富文本),它是从CView类派生来的。视图窗口与其父窗口——框架子窗口的客户区域对齐,它接收用户对应用程序的输入并显示相关联的文档数据。 • 对话框类CAboutDlg是从CDialog派生来的,该对话框类显示一个简单的版本信息的“关于” 对话框类。 (3) 运行MDI应用程序 • 编译并运行上述程序,建立两个文档MyWord1与MyWord2,其中MyWord1中存储的是一个Excel图表,MyWord2中存储的是离散数学中的“一阶谓词公式”。这是一个典型的多文档应用程序,同时打开了两个文档。单击“窗口|平铺”菜单项(该功能我们并没有编写代码,得益于MDI应用程序框架自动提供了该功能),则打开的两个文档视图平铺在框架窗口中,其显示的界面如图12.8所表示,看它是否和Word字处理程序的功能类似。

  16. 【例12-2】(续) 图12.8 MyWord的运行结果

  17. 12.1.3 从程序员角度看SDI与MDI应用程序 从程序员的眼光看,每个MDI应用程序必须有一个CMDIFrameWnd或其派生类的对象,该窗口称做MDI框架窗口。 CMDIFrameWnd是从CFrameWnd派生来的,它不仅继承了CFrameWnd类的全部特性外,还增加了一些与MDI相关的一些特性。

  18. 12.1.3 从程序员角度看SDI与MDI应用程序(续) (1)与SDI不同,MDI框架窗口并不直接与一个文档和视图相关联,MDI框架窗口有其自己的客户窗口,在显示或隐藏工具栏或状态栏时,重新定位该子窗口;(2)MDI客户窗口是MDI子窗口的直接父窗口,它负责管理主框架窗口(主应用程序窗口)的客户区(不包括菜单栏、工具栏、状态栏、滚动条的供客户使用的区域)以及创建子窗口。每个MDI主框架窗口有且仅有一个MDI客户窗口。(3)MDI子窗口是CMDIChildWnd或其派生类对象,CMDIChildWnd也是从CFrameWnd派生来的,主要用于容纳文档和视图,相当于SDI中的主框架窗口。每打开一个文档,主程序窗口就自动为该文档创建一个MDI子窗口。一个MDI应用程序负责动态地创建和删除MDI子窗口,在任何时候,最多只有一个子窗口是活动的。(4)在没有任何活动子窗口时,MDI的框架窗口可以有自己的默认菜单。当有活动的子窗口(打开的文档)时,MDI框架窗口的菜单会自动被子窗口菜单取代。框架窗口会自动监视当前活动文档窗口的类型,相应地改变框架窗口的菜单;(5)MDI框架窗口为层叠、平铺、排列和新建子窗口等一些标准的窗口操作提供了默认的菜单选项。用户单击文件下的“新建”命令时,框架窗口会调用CDocTemplate::CreateNewFrame()为当前的活动文档创建一个子窗口,该函数不仅创建子窗口(CMDIChildWnd类的对象),还创建与文档相对应的视图。

  19. 12.1.4 应用程序类 • 1.应用程序对象的声明 应用程序类CWinApp负责唯一的全局应用程序对象的创建、初始化、运行和退出清理过程。Windows应用程序有一个WinMain()函数,它是整个应用程序的起点。使用VC的应用程序架构创建的MFC程序已经封装了该函数。当程序被启动时,WinMain()被自动执行,进行一些标准的初始化工作,而把控制权转移给应用程序对象。应用程序类提供了如下的几个可超载的函数:初始化实例函数InitInstance()、处理消息的函数Run()、退出应用程序实例的函数ExitInstance()、 空闲时间的处理函数OnIdle()。 • 打开上述类图中的应用程序类CMyWord的实现文件,在其实例化函数InitInstance()前面有如下的一行代码: • CMyWordApp theApp;

  20. 12.1.4 应用程序类(续) • CMyWordApp是从CWinApp派生出来的应用程序类,theApp是该类的对象——应用程序对象。它是一个全局变量,先于其它对象被创建。该类有一个很重要的函数InitInstance(),在该函数中创建一个文档模板,来管理文档/视图结构涉及的框架窗口、文档和视图。2.文档模板文档模板负责在运行时动态创建文档、视图和框架窗口。一个应用程序对象可以管理一个或多个文档模板,每个文档模板用于动态创建和管理一个或多个同类型的文档(这取决于应用程序是SDI程序还是MDI程序)。MFC的文档模板类CDocTemplate用于支持文档模板操作,对于单文档界面程序,应使用CSingleDocTemplate(单文档模板类),对于多文档界面程序,使用CMultipleDocTemplate(多文档模板类)。文档模板定义了文档、视图和框架窗口这三个类的关系。通过文档模板,可以知道在创建或打开一个文档时,需要用什么样的视图和框架窗口显示它。这是因为文档模板保存了文档及其对应视图和框架窗口的CRuntimeClass对象指针。

  21. 12.1.4 应用程序类(续) • 此外,文档模板还保存了所支持的全部文档类的信息,包括这些文档的文件扩展名信息、文档在框架窗口中的名字、代表文档的图标等信息。下面为在应用程序类中的实例化函数中创建文档模板的代码: • CMultiDocTemplate* pDocTemplate; //声明一个指向多文档模 板的指针变量pDocTemplate • pDocTemplate = new CMultiDocTemplate( //创建多文档模板 • IDR_MULTIDTYPE, • RUNTIME_CLASS(CMyWordDoc), • RUNTIME_CLASS(CChildFrame), // custom MDI child frame • RUNTIME_CLASS(CMyWordView)); • AddDocTemplate(pDocTemplate); • //将创建的文档模板添加到应用程序对象中

  22. 12.1.5 文档/视图结构优点 • 文档/视图结构重要的特征是数据操作与数据显示分离。由于数据操作与数据显示分别按标准方法封装于两个不同类型的对象中,因而这两项任务的工作量都被大大简化了,各个对象单独存在,而不是将两种代码混在一起。一般说来,对那些有大量数据需要管理、或数据显示非常复杂的应用,文档/视结构显得尤为有用。 • 文档/视图结构另一个重要特征是许多MFC类对公共的文档/视图活动提供了大量的帮助。例如,对文件的操作(选择文件、打开文件和关闭文件)完全由框架类来管理。用户所需做的仅仅是从数据流中读写所需要的字节,这种工作方法可以省略相同代码的重复编写,而将更多的时间用于编写应用程序的特殊代码上。

  23. 12.2 框架——视图的容器 • 12.2.1 框架窗口的组成 • 12.2.2 框架窗口的创建和清除 • 12.2.3 框架窗口的子窗口 • 12.2.4 框架窗口与用户交互对象

  24. 12.2.1 框架窗口的组成 • 框架窗口是由窗口类来管理的,在SDI应用程序中,框架是从CFrameWnd类继承而来的,在MDI应用程序中,应用程序主窗口是从CMDIFrameWnd类继承而来的,应用程序的文档窗口是从CMDIChildWnd类继承而来的。 • 框架和框架所围的内容组成框架窗口。框架窗口包括应用程序主窗口和文档窗口。在单文档的应用程序中,应用程序主窗口和文档窗口是一致的。多文档应用程序中的框架窗口包括两个:一个是主框架窗口类CMainFrame,另一个是子框架窗口类CChildFrame,前者从多文档主框架窗口CMDIFrameWnd继承而来,后者从多文档子框架窗口CMDIChildWnd继承来。 • 框架窗口的作用主要有两个:一是为视图提供可视的边框,包括标题栏、一些标准的窗口组件(最大、最小化按钮、关闭按钮),像一个容器一样把视图包装起来。二是响应标准的窗口消息,包括最大化、最小化、调整尺寸等。当框架窗口关闭时,其中的视图对象也被自动删除。 • 对于框架窗口,程序要完成的主要工作是设置窗口外观,MDI应用程序还要维护活动文档窗口和非活动文档窗口之间的切换,对于框架所围的内容则由视图类来管理。

  25. 12.2.2 框架窗口的创建和清除 • 应用程序框架窗口都是使用动态建立宏来动态建立起来的对象。 • SDI应用程序只有一个框架窗口(应用程序主窗口和文档窗口合一),它随着应用程序的运行而创建,随着应用程序的结束而销毁。MDI应用程序的框架窗口包含应用程序主窗口和文档窗口:应用程序主窗口随着应用程序的运行而建立,随着应用程序的结束而销毁;文档窗口则是在用户打开文档或创建新文档时建立,文档关闭则文档窗口也随之关闭。 • 框架窗口创建的代码是在应用程序类的InitInstance()中完成的(参见7.3.4节中的内容),7.3.4节中的创建的MFC程序是一个基于框架的应用程序(不是基于文档/视图结构的),其中创建框架主窗口的代码为: • m_pMainWnd = new CMainWindow;

  26. 12.2.3 框架窗口的子窗口 • 对MDI的框架窗口中的主程序窗口来说,工具栏、状态栏等都是它的子窗口,各子窗口占据主窗口的用户区。除此之外,主程序窗口还含有一个特殊的子窗口——MDI客户窗口,它是MDI子窗口的直接父窗口,负责管理应用程序主窗口的客户区以及创建子窗口。每个MDI主窗口都有且仅有一个MDI客户窗口。 • MDI子窗口是CMDIChildWnd或其派生类的对象,CMDIChildWnd类也是CFrameWnd的派生类,用于容纳视图和文档。在任何时候,最多只有一个子窗口是活动的。其派生类MDI客户窗口,还有自己的子窗口——文档窗口。文档窗口同样有自己的子窗口——视图。父窗口管理子窗口的定位,并向子窗口传送命令消息。 • 在任何情况下,调用AfxGetMainFrame()函数可以获得应用程序的主窗口指针,通过该指针可以对框架窗口进行各种操作。

  27. 12.2.4 框架窗口与用户交互对象 • 前面章节已经讲了常见的交互对象——菜单、工具栏和状态栏,它们都是靠框架窗口来管理的。 • 工具栏按钮和菜单项的更新(使用ON_UPDATE_COMMAND_UI机制)消息可以在框架窗口类中处理,框架窗口还将状态栏定位在自己客户区的底部,并管理状态栏的显示单元。 • 对MDI应用程序,应用程序主窗口管理菜单栏和标题栏。当主窗口没有文档窗口时,主窗口显示一个缺省的菜单条;否则,主窗口的菜单条会被当前活动的文档窗口的菜单条取代。如果MDI程序支持多种文档类型,各类型的文档窗口用自身的菜单条来取代主窗口的菜单条,并改变主窗口的标题栏。

  28. 12.3 文档——管理和组织数据 12.3.1 一个汽车管理的SDI应用程序 12.3.2 构造汽车类CCar 12.3.3 自定义类CCar的序列化 12.3.4 文档类CDocument 12.3.5 文档类的设计 12.3.6 数据存盘——文档数据序列化

  29. 12.3.1 一个汽车管理的SDI应用程序 • 在前面12.1.2节中讲了一个不用编写一行代码的SDI应用程序,目的在于理解SDI的概念。其中的视图类是从CEditView继承而来的,该类实际上类似于一个文本编辑控件。这一节,我们要编写一个视图类从CView继承而来的SDI应用程序,该应用程序能够保存汽车的相关信息,该程序需要自己编写一部分代码,请读者认真体会。 • 【例12-3】本实例实现汽车信息的管理,用文档保存汽车信息,用视图显示汽车信息。该程序采用MFC应用程序向导生成,它是一个SDI的应用程序CarManage,向导的每一步都用默认的选项。

  30. 12.3.2 构造汽车类CCar • 本程序要在文档类存储汽车的信息, • 因此首先要构造一个汽车类CCar。 1.创建汽车类 2.汽车类CCar的构造 3.汽车类头文件Car.h的主要内容 4.汽车类实现文件Car.cpp的主要内容

  31. 创建汽车类 图12.9 创建CCar类 • (1)在ClassView面板中,右键单击顶端的“CarManage Classes”,在弹出的菜单中选择“New Class”选项。 • (2)在“New Class”对话框中,Class Type选择“Generic Classes”,类名为CCar,单击“Base Class(es)”列表框的第一行,输入CObject作为基类。如图12.9所示。 • (3)单击“OK”按钮,在接着出现的消息框中点击“OK”按钮,则在当前系统中增加了一个CCar类,这可以从ClassView中看到。

  32. 汽车类CCar的构造 • 汽车类CCar是汽车对象的抽象,我们主要关心汽车的牌照号码、车型、颜色和最高时速,因此要设置相应的成员变量来保存这些属性;另外还要添加设置和获取这些属性的成员函数。 • (1)在ClassView面板中,右键单击CCar类,在弹出的菜单中选择Add Member Variable,以增加成员变量。弹出图12.10所示的对话框。 • (2)设置变量的类型为CString,变量名为m_CarNumber,访问权限为Private。 • (3)重复同样的步骤(1)与(2),增加成员变量m_CarType、 m_MaxSpeed、m_Color。 • (4)在ClassView面板中,右键单击CCar类,在弹出的菜单中选择Add Member Function,以增加成员函数,弹出图12.11所示的对话框。 • (5)设置函数的声明为 • void SetCarValues(CString ,CString ,CString ,short ),如图12.11所示。单击“OK”按钮完成成员函数的增加。 • (6)重复同样的步骤(4)与(5),增加其它的成员函数GetColor()、SetColor(CString )、 Serialize(CArchive &ar)等。

  33. 图(三) • 12.11增加成员函数 图12.10增加成员变量图

  34. 汽车类头文件Car.h的主要内容 • 通过上面讲的步骤,会在汽车类的头文件Car.h中增加相应成员变量和成员函数的说明,当然用户也可以手工在该头文件中输入下列内容: class CCar : public CObject { DECLARE_SERIAL(CCar); //类CCar声明为可以串行化的类 public: CString GetCarNumber(){return m_CarNumber;} void SetCarNumber(CString CarNumber) {CarNumber=CarNumber;} CString GetCarType(); //取车型 void SetCarType(CString ); //设置车型 short GetMaxSpeed(); //取最高速度 void SetMaxSpeed(short);//设置最高速度 CString GetColor();//取车的颜色

  35. 汽车类头文件Car.h的主要内容(续) void SetColor(CString ); //设置车的颜色 void Serialize(CArchive &ar); //串行化函数 void SetCarValues(CString ,CString ,CString ,short ); //设置车的相关信息 CCar(); //构造函数 CCar(CCar &Car); //拷贝构造函数 virtual ~CCar(); //析构函数 private: CString m_CarNumber; // 车牌号,如晋0211 CString m_CarType; // 车型,如高尔夫 short m_MaxSpeed; // 最高时速 CString m_Color; // 车的颜色 };

  36. 汽车类实现文件Car.cpp的主要内容 • 类的实现文件里面包含了类的成员函数的实现信息,用VC创建的类也包含了Mocrosoft产品对C++的一些特有支持(它们与别的C++如Borland C++是不兼容的),下面为一些典型的成员函数的实现,其余的参见源程序。 (2)设置车的相关信息函数的实现: void CCar::SetCarValues(CString CarNumber,CString CarType, CString Color,short MaxSpeed ) { m_CarNumber=CarNumber; m_CarType=CarType; m_Color=Color; m_MaxSpeed=MaxSpeed; } (1)拷贝构造函数的实现: CCar::CCar(CCar&Car) { m_CarNumber=Car.m_CarNumber; m_CarType=Car.m_CarType; m_Color=Car.m_Color; m_MaxSpeed=Car.m_MaxSpeed; }

  37. 12.3.3 自定义类CCar的序列化 • 1.序列化概述 序列化实质上就是让对象将其当前的状态(由其成员变量的值表示)写入到永久性存储体(通常是指磁盘)中,以后还可以从永久性存储体中读取对象的状态(载入),从而重建对象。这种对象的保存和恢复的过程称为 “序列化”。序列化是一种实现对象持久性(Persistent)的机制,它是一个将对象中的数据转换成一个单一元素(通常是Stream)的过程。 • 2.自定义类的序列化 我们知道,利用面向对象的思想分析设计出来的程序中,要存盘的数据往往是以类对象的形式来存在的(例如前面汽车管理程序需要保存的汽车的信息就是以汽车类CCar来存储),如何将复杂的类对象中的数据存盘?我们是借助于CObject类与CArctive类来实现的。

  38. 12.3.3 自定义类CCar的序列化(续) • 在MFC的应用程序中,要想使程序员自己定义的类支持序列化,一般要做如下5步工作: • (1)从CObject类派生出自定义类,也就是准备序列化的类; • (2)重载自定义类的Serialize函数,加入必要的代码,用以保存自定义类对象的数据成员到CArctive对象以及从CArctive对象载入自定义类对象的数据成员状态; • (3)在类声明中加入DECLARE_SERIAL宏定义,这是序列化对象所必须的; • (4)提供一个缺省的构造函数; • (5)在类的实现文件.cpp中加入IMPLEMENT_SERIAL宏。 • 从前面的CCAr类的定义中可以看出该类是从CObject派生而来的,并且在头文件中也有DECLARE_SERIAL宏定义等,序列化的关键靠Serialize函数来实现,下面是其主要代码:

  39. 程序代码 • IMPLEMENT_SERIAL(CCar,CObject,1) //类的序列化的实现宏 • …… • void CCar::Serialize(CArchive &ar) //真正的序列化的代码 • { if (ar.IsStoring()) //如果是“写”磁盘的化 • { ar<<m_CarNumber<<m_CarType<<m_Color<<m_MaxSpeed; • //依次写入汽车对象的四个属性 • } • else //如果是“读"磁盘的化 • { ar>>m_CarNumber>>m_CarType>>m_Color>>m_MaxSpeed; • //依次读入汽车对象的四个属性 • }

  40. 12.3.4 文档类CDocument • 1.文档的定义 • 文档是程序处理的数据,可以利用File菜单上的Open命令来打开,并且可以利用Save命令保存的对象。文档用来存储数据,以及从存储磁盘文件中加载数据或将其存储到磁盘文件(永久性存储介质)。文档通常在框架窗口中显示并呈现在用户面前,用户可以通过它来操纵数据。 • 2.文档类CDocument • 文档类CDocument是MFC中负责管理文档的类,用户的文档类一般要从CDocument继承。该类封装了用户定义的文档类的基本功能,它支持标准的Windows操作,如建立、打开和保存文档等。该类的主要操作由表12.1的成员函数给出。

  41. 成员函数 功能说明 12.3.4 文档类CDocument(续) GetPathName 返回文档数据的路径 IsMidified 文档自上次保存以来是否已经修改 SetModifiedFlag 设置文档修改标志,表示文档已经修改 SetPathName 设置文档数据的路径 DeleteContents 清理文档 OnNewDocument 创建新文档,可超载该函数 OnOpenDocument 打开已有的文档,可超载该函数 OnCloseDocument 关闭当前活动的文档,可超载该函数 OnSaveDocument 将文档存盘,可超载该函数 • 表12.1 CDocument类的主要成员函数

  42. 12.3.5 文档类的设计 • 为了存储用户从数据录入界面所输入的汽车的信息,我们可以利用数组来存储。在MFC中,有一个对象数组类CObArray,它可以动态地调整自己的大小,以适应放在它里面的元素数量。该数组可以存放任何从CObject类派生出来的对象(CCar是从它派生而来,当然可以存储),另外我们还要向该数组中添加汽车对象,检索指定的汽车和取汽车的个数,因此在文档类中还要在增加几个成员函数实现这些功能。下面为在前面所生成的应用程序框架中的文档类CcarManageDoc中增加成员。 • 1.CCarManageDoc的成员 • 2.CCarManageDoc的成员函数的实现

  43. 在该类中增加一个存储汽车的对象数组m_oaCars和增加汽车函在该类中增加一个存储汽车的对象数组m_oaCars和增加汽车函 • 数AddCar()、取汽车的个数GetCarCount()、//取指定索引处的汽车。 • class CCarManageDoc : public CDocument • { • …… • protected: • CObArray m_oaCars; //存储汽车的对象数组 • …… • public: • short GetCarCount(); //取汽车的个数 • CCar * GetCar(short nIndex); //取指定索引处的汽车 • CCar * AddCar(CCar Car); //在对象数组中增加一辆汽车 • }

  44. CCarManageDoc的成员函数的实现在文档类CCarManageDoc的实现文件CarManageDoc.cpp中,下面为这些函数的实现: • CCar * CCarManageDoc::AddCar(CCar Car) • { //构造一个新汽车 • CCar *pCar=new CCar(Car); • //向对象数组m_oaCars中增加一个新汽车 • m_oaCars.Add(pCar); • //将文档标记为未保存(脏的,当关闭时,系统提示是否保存) • SetModifiedFlag(); • return pCar; //返回所增加的汽车 • } • CCar * CCarManageDoc::GetCar(short nIndex) • { • return (CCar *)m_oaCars[nIndex]; //从对象数组中取 • } • short CCarManageDoc::GetCarCount() • { return m_oaCars.GetSize(); • //取对象数组的大小,对象数组是动态调整的,其数组的大小就是当前元素的个数 • }

  45. 12.3.6 数据存盘——文档数据序列化 • 1.序列化的实现 • MFC的应用程序架构提供了数据序列化的方式来处理磁盘的存盘与打开,序列化的大部分工作靠应用程序框架来完成,我们所要做的就是重载文档类的序列化函数Serialize()。 • 由于文档类CCarManageDoc的祖先类也是CObject,因此要想使文档中的数据(对象数组m_oaCars)要想实现持久化,也是通过类的序列化函数Serialize()来实现的,下面是该函数的实现。 void CCarManageDoc::Serialize(CArchive& ar) {m_oaCars.Serialize(ar); /*调用对象数组CObArray的序列化函数来完成,对象数组又调用该数组中的元素CCar对象的序列化函数来完成,而CCAr的序列化函数在前面已经讲了*/ if (ar.IsStoring()) {// 普通成员变量的存盘,因为该文档没有普通成员变量,所以它不起作用 } else { // 普通成员变量的读取} }

  46. 12.3.6数据存盘——文档数据序列化(续) • 2.文档类序列化的局限 • 序列化机制这种保存数据的方法适合大多数的情况,特被是那些数据量不是特别大,而且需要一次性全部读入或写出的情况。 • 另外序列化只能顺序读写文件,不能进行随机操作,只能操作二进制文件,不能处理文本文件,不能操作数据库文件,不能共享式操作文件等。出现上述情况时,文档类的超载函数必须加以改变,需要通过CFile而不是CArchive来存取文件。CFile提供了文件打开、读、写、关闭和移动文件指针的函数。在文档类的Serializa()函数中,应用程序框架已经打开了文件,可以使用CArchive的成员函数GetFile()获取指向CFile对象的指针。另外,还有一个复杂但灵活的方法是超载应用程序类CWinApp的函数。

  47. 12.4.1 视图及其派生类12.4.2 在视图中绘图12.4.3 用户与视图进行交互 12.4 视图——显示文档数据,提供用户接口

  48. 12.4.1 视图及其派生类 • 1.视图定义 • 视图是数据的用户窗口,为用户提供了文档可视的数据显示,它把文档的部分或全部内容在窗口中显示出来。视图还给用户提供了一个与文档中的数据交互界面,它把用户的输入转化为对文档中数据的操作。 • 2.视图类CView • 视图类CView为用户定义的视图类提供了基本的功能,它被连接到文档上,用作文档和用户之间的媒介。视图是框架窗口的子窗口。 • 视图类CView是MFC中负责管理视图窗口的基类,其它视图类都由此派生。CView的主要功能是显示和修改文档的数据,它对文档数据的存储没有支持 • 从基类CView派生了如下常用的视图类: (1)CScrollView。滚动视类;(2)CEditView。编辑视图类;(3)CFormView。一种可以滚动的视图,其中包括了对话框控件;(4)CHtmlView。该类提供了Web浏览器的功能。 视图类CView的主要操作由表12.2的成员函数给出。

  49. 成员函数 功能说明 表12.2 CView类的主要成员函数 GetDocument 返回与视图相连接的文档 OnlnitialUpdate 在一个视图第一次与文档连接的时候调用 OnActivateView 当一个视图被激活时调用 OnDraw 调用它画出文档的图象,用于屏幕显示,打印或打印预览。该函数一般需要程序员提供其实现 OnPrint 打印或预览文档的一页 OnUpdate 调用这个函数以通知一个视图,文档已经被修改

  50. 12.4.2 在视图中绘图 • 在基于文档/视图结构的应用程序中,屏幕上所有的显示都是“画”出来的,而几乎所有的“画”动作都是靠视图类的OnDraw()函数来实现,该函数主要完成两个功能: 1.调用文档对象的成员函数GetDocument()获取与该视图所关联的文档的指针。 2.调用CDC的成员函数进行绘图操作,以“画”出文档数据。