第 5章 文档 /视结构文档 /视结构是 Microsoft公司为 Visual C++ Windows程序设计提出的一种新的编程机制。它是我们在 Visual C++
中使用 MFC开发基于文档的应用程序的基本框架,在这个框架中,数据的维护和显示是分别由两个不同的且彼此紧密相关的两个对象 ——文档和视负责的。在文档 /视结构中,文档相当于一个数据容器或数据管理中心,而视却相当于显示数据的窗口或者是和数据发生交互的窗口。因此一个完整的基于 MFC开发的应用程序一般至少由四个类组成( SDI应用),CWinApp应用类,
CFrameWnd窗口框架类,CDocument文档类,CView视类( Visual C++ V6.0中支持创建不带文档 -视结构的应用)。文档 /视结构在很多场合与传统的编程方式相比要更有利于这一类应用程序的编写。
在应用中一个视对应一个文档,但一个文档可以包含多个视 。
一个应用中只用一个框架窗口,对多文档界面来讲可能有多个
MDI子窗口 。 每一个视都是一个子窗口,在单文档界面中父窗口即是框架窗口,在多文档界面中父窗口为 MDI子窗口 。 一个多文档应用中可以包含多个文档模板,一个模板定义了一个文档和一个或多个视之间的对应关系 。 同一个文档可以属于多个模板,但一个模板中只允许定义一个文档 。 同样一个视也可以属于多个文档模板 。
应用程序对象
Doc文档模板 A Doc2文档模板 B
Doc1实例 Doc2实例 Doc3实例 Doc1实例 Doc2实例图 5.2 有两种文档类型的 MDI应用程序文档、视和应用程序框架
Visual C++ 6.0 是一个功能强大,用户界面友好而倍受程序员青睐的开发工具 。 但是,在当前的 Microsoft 基本类库 4.x 版本中,大约有将近 200多个类,数千个函数,加之 Microsoft 公司隐藏了一些技术细节
,使得人们深入学习 MFC变得有些困难 。 实际上,在 Visual C++开发平台下,MFC的 AppWizard可以生成三种类型的应用程序:
l 基于对话框的应用
l 单文档应用 ( SDI)
l 多文档应用 ( MDI)
SDI和 MDI均是文档 /视窗结构的应用程序类型。早些时候,
MFC应用程序模型只是使用应用程序对象和主窗口对象的简单模型。
在这个模型中,应用程序的数据作为成员变量保持在框架窗口类中,
在框架窗口的客户区中,该数据被输出到显示器。随着 MFC新版本问世,一种新式应用程序结构 ——文档 /视结构出现了。在这种结构中,
CFrameWnd繁重的任务被委派给几个不同类,实现了数据存储和显示的分离。
一般情况下,采用文档 /视结构的应用程序 ( 如 SDI应用 ) 至少应由以下 4个对象组成:
应用程序对象:该对象是一个从 CWinApp类派生的类的对象,它是应用程序的中心 。 应用程序对象将消息沿消息映射网络分配给它的所有子程序 。
框架窗口对象:该对象是一个从 CFrameWnd 类派生的类的对象 。
文档对象:该对象是一个从 CDocument类派生的类的对象,
它主要用来存储应用程序中的数据,并把这些信息提供给应用程序的其它部分 。
视窗对象:该对象是从 CView类派生的类的对象,它与其父框架窗口用户区对齐。视窗接受用户对应用程序的输入并显示相关联的文档数据。
通常,应用程序数据存放于简单模型中的框架窗口中 。 在文档 /视结构应用中,该数据被放在称为文档的独立数据对象中 。 当然,文档不一定就是文字,文档可以是表现应用程序使用的数据集的抽象术语 。 而用户输入处理及图形输出功能从框架窗口转向视图 。 单独的视窗完全遮蔽框架窗口的客户区,这意味着即使程序员直接绘画至框架窗口的客户区,
视图仍遮蔽绘画,在屏幕上不出现任何信息 。 所以输出必须通过视图 。
框架窗口仅仅是个视图容器 。 注意,Visual C++可以建立只有视窗而没有文档的应用程序框架 。
CDocument类对文档的建立及归档提供支持并提供应用程序用于控制其数据的接口 。 MDI应用程序可以处理多个类型的文档,每个类型的文档拥有一个相关联的文档模板对象 。 文档对象隐藏在视窗的后面,提供由视图对象显示的信息 。 文档至少有一个相关联的视图 。 视图只能与一个文档相关联 。
在文档 /视方式中,对象的建立是由文档模板来管理的,它是
CDocTemplate类派生类的对象,建立并维护框架窗口,文档及视 。
总之,在文档 /视方式中,文档和视是分离的,即:文档用于保存数据,
而视是用来显示这些数据 。 文档模板维护它们之间的关西 。 这种文档 /视结构在开发大型软件项目时特别方便,有用 。
在文档 /视应用程序中,CWinApp派生类对象拥有并控制文档模板,文档模板产生文档、框架窗口及视窗。从用户的角度来看,视窗实际上是一个普通的窗口。象其他基于 Widnows应用程序的窗口一样,人们可以改变它的尺寸大小,对它进行移动,也可以随时关闭它。若从程序员的角度来看,视窗实际上是一个从 MFC类库中的 CView类所派生出的类的对象。文档对象是用来保存数据的,而视窗对象是用来显示数据的,并且允许对数据进行编辑。 SDI或 MDI的文档类是由 CDocument类派生出来的,它可以有一个或多个视类,而这些视类最终都是由 CView类派生出来的。视对象只有一个与之相联系的文档对象,它所包含的 CView::GetDocument函数允许应用在视中得到与之相联系的文档,据此,应用程序可以对文档类成员函数及公共数据成员进行访问。如果视对象接受到了一条消息,表示用户在编辑控制中输入了新的数据,此时,视就必须通知文档对象对其内部数据进行相应的更新。如果文档数据发生了变化,则所有的视窗都必须被通知到,以便它们能够对所显示的数据进行相应的更新。
Document::UpdateAllViews函数即可完成此功能。
操作事件与应用程序框架我们在 Windows应用中,输入数据主要是通过鼠标,键盘和点击菜单条和工具条来进行人机交互的 。 操作事件当然与这些输入数据操作有关 。
鼠标操作是目前 Windows 软件的主要人机互动方式 。 Windows软件根据鼠标动作产生的消息作相应处理 。 因此,鼠标消息是我们经常要处理的消息,
其消息主要分为:鼠标移动,按钮按下,松开,双击等消息 。 我们可以利用
ClassWizard为这些消息添加相应的消息映射 。
键盘同鼠标一样是 Windows应用程序共享的全局硬件资源,键盘事件是由消息,消息映射极其消息处理函数来处理的 。 Windows 发送键盘消息给具有输入焦点的窗口,通知窗口用户正击什么键,通常这些消息有:
WM_KEYDOWN和 WM_KEYUP。 除 Alt外,所有键发送 WM_KEYDOWN和
WM_KEYUP消息 。 利用菜单接受用户命令是一种很简单的交互方法,同时也是一种很有效的方法 。 通常菜单作为一种资源存储在文件中,因此我们可以在设计时就利用资源编辑器设计好一个菜单 。 关于使用 Visual C++ 设计菜单前面已述这里我就不再多讲了,但这里我要提醒大家是你在编写菜单时应该尽量在属性对话框的底部提示 ( Prompt) 处输入文字,这虽然不是必要的,
但 MFC在有状态栏和工具条的情况下会使用该文字,文字的格式为,状态栏显示说明 \n工具条提示,。
分割器视窗分割器窗口是 Windows NT/95 下编程常用的一种视组织技术,它用相同的视或不同的视对同一种文档进行表达,
大家在 Microsoft Word和 Microsoft Excel应用程序中使用的分割器分割窗口是一种通常叫做动态分割器分割窗口的应用环境;而在 Windows NT/95下,使用的资源管理器里也有分割器,这种分割器通常叫做静态分割器。在本文中主要讨论这些分割器窗口实现原理、方法、技术,这种技术的掌握可以用来开发我们自己的应用软件的用户界面和用户的操作环境。
Visual C ++ 类 CSplitterWnd 简介类 CSplitterWnd 是 MFC类库中实现分割器窗口的基类,根据需要可以从该类派生自己的分割器窗口类 。 分割器窗口是含有多个窗格的窗口,
每个窗格通常是应用程序特定的由 CView派生类的对象,也可以是具有适当子窗口 ID 的任何 CWnd对象 。 类 CSplitterWnd是类 CWnd的派生类,
因此,类 CSplitterWnd除了继承类 CWnd方法外,还有以下方法:
构造函数:
CSplitterWnd 该函数构造一个类 CSplitterWnd的对象
Create 该 函数 创建动 态分 割器窗 口并 将此窗 口连 接给 类
CSplitterWnd的对象
CreateStatic该函数创建静态分割器窗口并将此窗口连接给类
CSplitterWnd的对象
CreateView 该函数在分割器窗口中创建一个窗格操作函数:
GetRowCount 返回当前窗格的行计数值
GetColumnCount返回当前窗格的列计数值
GetRowInfo 返回指定行上的信息
SetRowInfo 设置指定行上的信息
GetColumnInfo 返回指定列上的信息
SetColumnInfo 设置指定列上的信息
GetPane 返回指定行列处的窗格
IsChildPane 确定一个窗口当前是否是这个分割器窗口的子窗口
IdFromRowCol 返回指定行列处的窗格的子窗口 ID 值
RecalcLayout 在调整行或列尺寸后,调用该函数重新显示该分割器窗口
GetScrollStyle返回共享滚动条的风格
SetScrollStyle 为该分割器窗口共享的滚动条指定新的滚动条风格分割器视窗的实现单文档应用程序和多文档应用程序的分割器窗口实现技术基本相同,只是基于父框架窗口类不一样而已。单文档应用程序嵌入一个 CSplitterWnd成员变量是基于父框架窗口类
CMainFrame类,CMainFrame类是 CFrameWnd的派生类;而多文档应用程序是基于 CMDIChildWnd的派生类,用 AppWizard
生成的多文档应用程序嵌入一个 CSplitterWnd成员变量是基于
CChildFrame类,CChildFrame类是 CMDIChildWnd的派生类;
当然也可以基于用 ClassWizard 生成的其他 CMDIChildWnd的派生类。
数据文件的读写我们在实现的应用程序中通常需要对数据进行保存,
或是从外部存储介质上读取数据,这就涉及到文件的操作。
我们可以利用各种文件存取方法完成这些工作,但 MFC中也提供了一种读写文件的简单方法 ——“序列化”(即
Serialize,该函数为虚函数)。序列化机制通过更高层次的接口功能向开发者提供了更利于使用和透明于字节流的文件操纵方法,举一个例子来讲你可以将一个字符串写入文件而不需要理会具体长度,读出时也是一样。你甚至可以对字符串数组进行操作。在 MFC提供的可自动分配内存的类的支持下你可以更轻松的读 /写数据。你也可以根据需要编写你自己的具有序列化功能的类。
基于框架结构的序列化在一个基于 MFC的 Windows应用程序中,无论是保存文档还是打开文档,应用程序都是通过调用文档类的 Serialize成员函数来完成串行化操作的。因此,在大多数情况下,我们都需要通过重载 Serialize成员函数来实现文档的串行化。 Serialize成员函数带有一个
CArchive类型的参数,这是一个与所打开的文件相关联的对象。一般情况下,我们总是使用 CArchive对象来保存和打开文档。
实现一个类,怎样使其具有序列化功能呢? 你需要做以下的工作:
1,该类从 CObject派生 。
2,在类声明中包括 DECLARE_SERIAL宏定义 。
3,提供一个缺省的构造函数 。
4,在类中实现 Serialze函数
5,使用 IMPLEMENT_SERIAL指明类名和版本号定制序列化定制序列化是区别上述序列化的一种实现数据存储的序列化方法,定制序列化进行数据存储与框架无关即数据文件存储与文档类无关 。 通常与以下类相关:
l CFile 文件类
l CStdioFile 标准 I/O文件类
l CArchive 文档类
MDI多类型文档的应用一个复杂的 Windows应用程序的实现就有可能存在多种类型视窗组织问题 。 当应用中需要多种视窗表现数据和图形时,这就涉及到 MDI
多类型文档的实现技术 。 要理解 MDI多类型文档视窗组织技术首先我们得了解文档模板对象 。
文档模板能够使所创建的文档、视和边框窗口和谐的结合起来。
一种特定的文档模板类创建并管理着已打开的同种类型的所有文档。
支持多种类型文档的应用程序要有多个文档模板。如果是 SDI应用程序要使用 CSingleDocTemplate类。 如果是 MDI应用程序则要使用
CMultiDocTemplate类。在 SDI应用程序中,根据已建立的文档、视和边框窗口创建一个 CSingleDocTemplate类的对象。在 MDI应用程序中,
根据已建立的文档、视和子边框窗口创建一个 CMultiDocTemplate类的对象。
基于框架编程建立一个 MDI应用程序至少有两个框架窗口类,一个为主边框类,
另一个为子边框类。子边框类可以有多个子边框对象,用 AppWizard创建的应用程序所创建的边框窗口对象的情况如下表所示:
基类 AppWiz
ard创建类对象数目 菜单和控制栏包含的视类对象的创建
CMDIFra
meWnd
CMainFr
ame
1:仅一个对象有 无 在应用类
InitInstance
函数中创建
CMDICh
ildWnd
CChildFr
ame
N,(每个子窗口一个 )
无 有 当新的子窗口被打开时,由应用程序框架创建
MDI应用程序中的应用类里的 InitInstance函数可以通过多次调用 AddDocTemplate函数支持多个文档模板,每个模板可指定不同的文档类,视图类和 MDI子边框窗口类进行组合 。 当用户从多文档模板应用程序的 File菜单中选择
New时,应用程序框架会显示一个列表框,允许用户根据字符串资源中所指定的名字 ( 文档类型的子字符串 ) 来选择一个模板 。
注意:在 SDI应用程序中并不支持多次 AddDocTemplate的调用,因为此时在应用程序的生存周期内,文档对象、视图对象和边框窗口对象都只能被创建一次。
基于技术内幕的实现文档模板类是 Micrisoft MFC面向用户的封装类。
其实,CSingleDocTemplate 和 CMultiDocTemplate
都是从 CDocTemplate派生的类;而 CDocTemplate
又是从 CDocManager类派生而来的,CDocManager
类是不面向用户的。 OpenDocumentFile(szPath)函数是 CDocManager类的成员函数。当我们建立了
CMultiDocTemplate对象之后,我们可以直接调用
OpenDocumentFile(szPath)函数,使其封装的视窗显示。这应该是技术内幕。