点击下载 - Visual C++ 程序设计精品课程

Download Report

Transcript 点击下载 - Visual C++ 程序设计精品课程

第3章
文档和视图
引言





了解文档与视图结构程序的工作原理
掌握应用程序中的菜单界面设计方法
掌握工具栏和状态栏等程序界面的设计方法
了解文档的序列化工作原理
了解使用不同的视图类编程
3.1 文档与视图结构
3.1.1 文档与视图结构概述



在文档—视图结构中,文档的任务是对数据进行管理和维护。
数据通常被保存在文档类的成员变量中。在Visual C++ 2005
中,文档类通过一个称为串行化(serialize)的过程将数据保存
到磁盘文件或数据库中。
MFC类库为数据的串行化提供了默认的支持,只需要在此
基础上稍加修改,就可以为自定义的文档类提供串行化支持。
在MFC中主要有两种类型的文档视图结构,即单文档界面
(SDI:Single Document Interface)应用程序和多文档界面
(MDI:Multiple Document Interface)应用程序。

SDI应用程序的5个基类之间的关系模型如图3.1所示。
应用对象
CWinApp
指针
创建
文档模板
CSingleTemplate
创建
指针
应用对象
CDocument
指针
创建
框架窗口
CFrameWnd
创建
指针
视图对象
CView
指针
图3.1 SDI的基本类结构布局

MDI应用程序的6个基类之间的关系模型如图3.2所示
应用对象
CWinApp
应用程序框架窗口
CMDIFrameWnd
创建
指针
指针
文档模板
CMultiDocTemplate
创建
指针
父窗口
创建
子窗口
应用对象
CDocument
指针
子框架窗口
CMDIChildWnd
创建
指针
视图对象
CView
指针
图3.2 MDI的基本类结构布局

视图在文档和用户之间起中介作用,它只负责实现和修改文
档数据,但不负责存储。

一个视图是一个没有边框的窗口,它位于主框架窗口区的客
户区。视图是文档对外显示的窗口,但是它不能完全独立,
必须依存在一个框架窗口内。

每一个文档可以有多个视图,但每个视图只能对应于一个确
定的文档。

视图是文档的不同表现形式。
3.1.2 文档与视图间的相互作用

文档与视图的交互是通过类的共有成员变量和成员函数来实现的。表3.1
给出了文档—视图结构中各类对象相互访问所采用的成员函数。
当前对象
被访对象
成员函数
文档
视图
GetFirstViewPosition和GetNextView
文档
模板
GetDocTemplate
视图
文档
GetDocument
视图
框架
GetParentFrame
框架
视图
GetActiveView
框架
文档
GetActiveDocument
MDI主框架
MDI子框架
MDIGetActive
MDI子框架
MDI主框架
GetParentFrame
任何位置
应用程序
AfxGetApp
任何位置
主框架
AfxGetMainWnd
表3.1
对象访问所需函数
3.1.3 多文档应用程序

Visual C++ 2005中的一个多文档应用程序有一个主窗口,在
主窗口中可以同时打开多个子窗口,每一个子窗口对应一个
不同的文档。

利用MFC 应用程序向导可以很方便地建立一个多文档应用
程序。

1.多文档应用程序

【例3.1】编写一个多文档应用程序MyMdi,程序运行后在
客户区窗口显示信息“这是hello world的多文档程序!”。

程序的实现过程如下:

(1)创建应用程序框架
(2)添加代码
在MyMdi应用程序的CMyMdiView类的OnDraw函数中添加
显示文本的代码
pDC->TextOut(50,50, _T("欢迎学习建立多文档程序!"));
TextOut函数用于在指定的位置输出字符串。





2.文档模板

在文档-视图结构应用程序中,数据以文档类的对象形式
存在。

MFC提供了抽象基类CDocTemplate来实现文档模板的功
能,应用程序不能直接使用,但可以把文档模板定义为
CDocTemplate的派生类,在应用程序中,必须为每种类
型的文档建立一个文档模板。

(1)文档模板的构成
 文档模板定义了三个类之间的关系。
 ① 文档类
 应用程序从CDocument类派生此类,用于完成文档的
新建、打开和保存等工作。
 ② 视图类
 应用程序从CView、CScrollView、CEditView类派生
而来,用于显示文档数据。
 ③ 含有该视图文档的框架窗口类
 单文档应用程序从CFrameWnd类派生此类,多文档
应用程序从CMDIChildWnd类派生而来。如果应用程
序不需要定制框架窗口,则也可以直接使用
CFrameWnd类和CMDIChildWnd类。

MFC除了提供了CDocTemplate抽象基类之外,还提供了
CDocTemplate 的 两 个 派 生 类 : CSingleDocTemplate 和
CMultiDocTemplate。
 CSingleDocTemplate类定义了一个实现单文档界面的文
档模板。SDI应用程序使用主框架窗口来显示一个文档,
每次只能打开一个文档。
 CMultiDocTemplate类定义了一个实现多文档界面的文
档模板。MDI应用程序用主框架窗口作为工作空间,在
其中可以打开单个或多个文档框架窗口。在每个文档窗
口中显示一个文档。

(2)文档模板的创建

在初始化应用程序时,必须首先注册文档模板,以便程序利
用这个模板来完成主框架窗口、视图、文档对象的创建和资
源的装入。

在SDI或MDI应用程序中,要注册文档模板,需通过new运
算 符 调 用 文 档 模 板 类 的 构 造 函 数 生 成 一 个
CSingleDocTemplate或CMultiDocTemplate类对象,并调用
函数AddDocTemplate注册该文档模板对象。
在函数 InitInstance 中创建
应用程序对象
文档模板对象
创建
文档对象
创建
创建
框架窗口对象
视图对象
创建
图3.4
应用程序中各对象之间的创建关系
通过函数 GetActiveView 访问
框架窗口对象
视图对象
通过 CWnd::GetParentFrame 访问
文档模板对象
通 过 GetFirstViewPosition
通过 GetDocument 访问
和
GetNextView 访问
文档对象
通过 GetDocTemplate 访问
通过 GetActiveDocument 访问
图3.5
应用程序中各对象之间的访问方法
3.2 菜单设计

菜单分为两类:
 一类是依附于框架窗口的一般菜单,包括主菜单和子
菜单,主菜单(菜单栏)横放在窗口的顶部,它是应
用程序的最高层菜单,子菜单是从主菜单下弹出的菜
单。
 另一类是弹出式菜单,也叫快捷菜单或上下文菜单,
它是点击鼠标右键后,在光标所在位置出现的浮动式
自由菜单。
3.2.1 建立菜单资源

菜单是Windows应用程序中一个必不可少的用户界面资源,
Visual C++ 2005集成开发环境提供了一个可视化菜单编辑器
用于菜单的编辑和添加。

菜单中的每一个菜单项都由菜单项名(即Caption)和命令
ID号两个基本要素组成。


创建菜单可以有好几种方法,最简单的方法是在属性窗
口用菜单编辑器来进行设计。
利用菜单辑器创建菜单资源的主要步骤如下:
 (1)打开应用程序的菜单编辑器;
 (2)添加主菜单;
 (3)添加菜单项,设置菜单项的属性。

【例3.2】编写一个应用程序MyMenu,为程序添加一个
“图像”主菜单和它的两个下拉子菜单项“显示”和
“删除”。

程序的实现过程如下:

(1)创建应用程序,打开菜单编辑器
(2)添加主菜单
(3)添加菜单项,设置菜单项的属性


3.2.2 添加命令处理函数

Windows应用程序是通过消息传递机制运作的,菜单项对命
令的激发、调用是通过发送WM_COMMAND消息实现的。

处理的原则是:依据菜单的不同作用进行不同的映射。
 如果该菜单用于文档的显示编辑,则最好在视图类中映
射。
 如果该菜单用于文档的打开和存储,则最好在文档类中
进行映射。
 对于通用菜单,可以在框架类中映射。

在【例3.2】中只添加了菜单资源,并没有实现菜单的功能,
即没有对应的命令处理函数与菜单项对应,因此,程序运行
后添加的菜单项是灰色的,即处于当前不可用状态。添加新
的菜单项后,还应该为新的菜单项指定一个处理函数。




(1)打开类视图找到相应的类。
(2)单击其属性窗口中的“事件”按钮,此时属性窗
口将列出此类可以处理的对象ID。
(3)从“菜单命令”中选择ID_SHOWPICTURE,并
单击其左侧的“+”号,就会出现可处理的命令消息。
(4)选择适当的消息,单击其右侧的单元格,就会出
现提示信息
快捷键的添加需要使用快捷键编辑器,添加快捷键的步骤
如下:



(1)打开应用程序的加速器表;
(2)添加新的快捷键;
(3)设置快捷键的属性。


Visual C++ 2005的MFC提供的菜单类的对象可以用来表示
各种Windows菜单,并且该类可以在程序运行时处理有关菜
单的操作。
1. 创建菜单
 CMenu类的成员函数CreateMenu和CreatePopupMenu分别用来创建
一个菜单或子菜单框架,它们的函数原型如下:
 BOOL CreateMenu();
//产生一个空菜单
 BOOL CreatePopupMenu();
//产生一个空的弹出式子菜单

2. 装入菜单
 CMenu类的成员函数LoadMenu可用来将菜单资源装入
应用程序中,它的函数原型有两种:
 BOOL LoadMenu(LPCTSTR lpszResourceName);
 BOOL LoadMenu(UNIT nIDResource);


3. 添加菜单项
当菜单创建后,可以调用CMenu类提供的成员函数
AppendMenu或InsertMenu向菜单中添加一些菜单项
 它们的函数原型如下:
 BOOL
AppendMenu(UNIT nFlags, UNIT nIDNewItem,const
CBitmap * pBmp);
 BOOL AppendMenu(UNIT nFlags, UNIT nIDNewItem=0,LPCTSTR
lpszNewItem=NULL);
 BOOL
InsertMenu(UNIT nPosition, UNIT nFlags, UNIT
nIDNewItem,const CBitmap *pBmp);
 BOOL
InsertMenu(UNIT nPosition, UNIT nFlags, UNIT
nIDNewItem=0,LPCTSTR lpszNewItem=NULL);
 BOOL
InsertMenuItem(UINT
uItem,
LPMENUITEMINFO
lpMenuItemInfo,BOOL fByPos = FALSE);

4.删除菜单项
 当要删除指定的菜单项时,可利用CMenu类的成员函
数DeleteMenu来完成,该函数的原型为:
 BOOL DeleteMenu(UNIT nPosition,UNIT nFlags);

5.获取菜单项
 可以利用CMenu类的以下3个成员函数分别获得菜单
的项数、菜单项的ID号以及弹出式子菜单的句柄。
 GetMenuItemCount()函数
 GetMenuItemID()函数
 GetSubMenu()函数
3.2.3 弹出式菜单

弹出式菜单是通过利用CMenu类和其成员函数,在程序运
行过程中动态建立的。弹出式菜单的建立方法有两种:
 (1)利用现有的菜单项进行创建。
 (2)为弹出式菜单专门建立一个菜单资源,通过调用函
数CMenu::LoadMenu装入创建的菜单资源。
弹出式菜单的实现过程如下:
 (1)利用成员函数CMenu::CreatePopupMenu创建一个
弹出式菜单。
 (2)利用成员函数CMenu::LoadMenu装入菜单资源或
利用成员函数CMenu::AppendMenu添加菜单项。
 (3)利用成员函数CMenu::TrackPopupMenu在指定位
置显示弹出式菜单,并跟踪用户的菜单项选择。
TrackPopupMenu成员函数的原型为:
 BOOL TrackPopupMenu (UNIT nFlags, int x, int y,
CWnd *pWnd, LPCRECT lpRect=0);

【例3.6】为【例3.5】的程序MyMenu的“图像”菜单添加
弹出式菜单。编译运行该程序,单击鼠标右键并松开后得到
如图3.12所示的结果。
图3.12
弹出式菜单

程序的实现过程如下:

(1)打开【例3.5】的例子,在Visual C++ 2005中,选择
“文件”菜单中的“打开”菜单项下的“项目/解决方
案”,打开上例中的“MyMenu.sln”文件。

(2)在类视图中选择类CMyMenuView,在属性窗口单
击“消息”按钮,选择WM_CONTEXTMENU,单击右
侧下拉菜单中的“<添加>OnContextMenu”选项,就在视
图类CMyMenuView中添加了消息
WM_CONTEXTMENU的消息映射函数。
3.3 工具栏和状态栏设计

工具栏和状态栏是Windows系统中常用的两种界面元素。

一般情况下,在创建应用程序框架时,MFC自动为应用程
序提供了一个标准工具栏和一个标准状态栏。
3.3.1 添加工具栏

工具栏是一系列工具按钮的组合,也是一种常用的命令输入
方式,用户可以用鼠标单击工具栏上的工具按钮来向应用程
序发出命令。

MFC工具栏还支持工具提示(Tooltip),工具提示用于帮助用
户理解单个工具栏按钮的作用。

工具栏也是资源,在Visual C++ 2005中创建工具栏资源 。

1.创建工具栏资源

创建工具栏资源的步骤如下:
 (1)打开应用程序的工具栏编辑器;
 (2)添加工具栏按钮;
 (3)设置工具栏按钮属性。

【例3.7】为【例3.6】的程序MyMenu的“图像”菜单项添
加工具栏按钮。程序运行后,工具栏的实际效果如图3.13所
示,新添加的工具栏按钮位于工具栏的最后两个位置。
图3.13
MyMenu的工具栏

程序的实现过程如下:

(1)打开【例3.6】的例子,在Visual C++ 2005中,选
择“文件”菜单中的“打开”菜单项下的“项目/解决方
案”,打开上例中的“MyMenu.sln”文件。

(2)在“资源视图”中展开“Toolbar”,双击其下的
IDR_MAINFRAME ,打开工具栏编辑器,屏幕上同时
显示工具栏设计窗口。

(3)添加工具栏按钮。

(4)设置工具栏按钮属性。

2.动态生成工具栏

动态生成工具栏的方法如下:

(1)声明一个CToolBar类对象。

(2)调用函数CToolBar::Create或CToolBar::CreateEx生
成指定风格的工具栏并与CToolBar类对象相连接。

(3)调用函数CToolBar::LoadToolBar装入工具栏。

【例3.8】为【例3.6】的程序MyMenu动态生成工具栏,
程序的实际运行效果如图3.18所示。
图3.18
动态生成程序MyMenu的工具栏

程序的实现过程如下:

(1)打开【例3.6】的例子,在Visual C++ 2005中,选择
“文件”菜单中的“打开”菜单项下的“项目/解决方
案”,打开上例中的“MyMenu.sln”文件。

(2)添加一个工具栏资源。

(3)如【例3.7】的方法设置工具栏按钮的属性。

(4)加载工具栏资源。
3.3.2 定制状态栏

MFC提供另一种经常与输入命令相关的用户界面对象:
状态栏。

状态栏是典型的位于应用程序主窗口底部的窗口,经常
在状态栏显示文本提示信息。

MFC把状态栏封装在状态栏类CStatusBar中,动态创
建状态栏的过程如下:
 (1)构造一个CStatusBar类对象。
 (2)调用Create函数创建一个状态栏窗口,并将该
窗口连接到该 CStatusBar类对象 上。
 (3)调用SetIndicators函数设置指示器ID。

利用向导创建应用程序时,在CMainFrame类中定义了一个
成员变量m_wndStatusBar,它是状态栏类CStatusBar的对
象。在MFC应用程序框架的实现文件MainFrm.cpp中,为
状态栏定义了一个静态数组indicators,如下所示:
static UINT indicators[] =
{
//定义分隔符,作为提示信息行的面板标志
ID_SEPARATOR,
ID_INDICATOR_CAPS,
//大写指示器面板标志
ID_INDICATOR_NUM,
//数字指示器面板标志
ID_INDICATOR_SCRL,
//滚动指示器面板标志
};

创建状态栏资源的具体步骤如下:
 (1)打开应用程序的串表编辑器;
 (2)添加新的串表,设置其属性;
 (3)添加指示器面板。

【例3.9】修改【例3.7】的程序MyMenu,在状态栏上显示
鼠标的位置,以及当前的状态(是显示图片还是删除图片)。
程序的实际运行效果如图3.19所示。
图3.19
MyMenu的状态栏

程序的实现过程如下:

(1)打开【例3.7】的例子,在Visual C++ 2005中,选择
“文件”菜单中的“打开”菜单项下的“项目/解决方
案”,打开上例中的“MyMenu.sln”文件。
(2)添加串表。
(3)添加指示器面板。
(4)添加消息映射函数OnMouseMove。
(5)修改OnDraw函数,添加显示当前状态的代码。




3.4 文档的读写

用户处理的数据往往需要存盘做永久备份,在MFC应
用程序中提供了数据序列化(serialize)的方式来处理文
档的读写功能。

序列化指的是从一个持久性存储介质读出或者写入一
个对象的过程。
3.4.1 序列化工作原理

文档类存盘的重点在于存储文档类中的成员变量,使之可以
利用这些变量完成重建,而不损失任何信息。

同时文档还需要能够从存储的文档文件中读取信息以便重建
文档类。

序列化的基本思想是:
 一个类应该能够对自己的成员变量的数据进行读写操
作,对象可以通过读操作而重新创建。
 由于CObject类提供了保存和加载对象状态的基本功能,
所以一般类的对象都具备将状态值写入磁盘或从磁盘
中读出的方法,这种对象的保存和恢复的过程称为序
列化。

一 个 可 序 列 化 的 类必 须 有 一个 称 作 为序 列 化 的成 员 函 数
Serialize,文档的序列化在文档类的成员函数Serialize中进
行。MFC应用程序向导在生成应用程序框架时只创建了文
档派生类序列化函数Serialize的框架,如下所示:
void CMyDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: 在此添加存储代码
}
else
{
// TODO: 在此添加加载代码
}
}

程序员要做的就是重载文档类的Serialize,并在函数中加
入读和写的代码。

C++主要通过文件句柄来实现磁盘输入和输出,一个文件
句柄与一个磁盘文件相关联。

文件数据的序列化归根到底是类的序列化,可序列化的类
必须直接或间接从类CObject派生,在类的头文件中,还
必须包含DECLARE_SERIAL宏调用,在类的实现文件中
必须包含IMPLEMENT_SERIAL宏调用。
3.4.2 MFC应用程序的序列化

MFC应用程序实现类的序列化通常需要以下几个步骤:
1. 可序列化类从CObject类或者CObject类的派生类派生;
2. 重载CObject类的成员函数Serialize();
3. 在类声明中使用DECLARE_SERIAL宏;
4. 定义一个无参数的构造函数;
5. 在类实现文件中使用IMPLEMENT_SERIAL宏。

【例3.10】编写一个应用程序MyDrawLine,程序运行后,
当用户在客户区窗口按下鼠标左键并移动时,根据鼠标移动
的轨迹绘制指定的线段。并且能将绘制好的图形保存在磁盘
上。程序的实际效果如图3.20所示。
图3.20
程序MyDrawLine的绘制线段效果







程序的实现过程如下:
(1)创建应用程序
利 用 MFC 应 用 程 序 向 导 创 建 一 个 SDI 应 用 程 序
MyDrawLine。
(2)实现绘制线段功能
① 为矩形定义一个新类Cline。
② 在视图类的头文件中定义两个公有变量,在视图类的
构造函数中初始化这两个公有变量,分别记录线段的起
点和终点:
③
在 视 图 类 中 为 WM_LBUTTONDOWN 、
WM_MOUSEMOVE和WM_LBUTTONUP消息分别添
加 消 息 映 射 函 数 OnLButtonDown 、 OnMouseMove 和
OnLButtonUp。

(3)文档数据系列化

① 在CLine类的头文件中加入以下代码:
② 在CLine类的实现文件中加入Serialize函数的代码:
③ 在文档类CMyDrawLineDoc的头文件中加入下面的代
码:
④ 在文档类CMyDrawLineDoc的Serialize函数中加入代
码。




(4)在视图类的OnDraw函数中加入下面的代码,其
中粗体部分为新添加的代码。

(5)修改视图类的OnLButtonUp函数。

(6)设置文档类型。
3.5 使用不同视图

MFC为应用程序提供了多种不同的视图,在利用MFC 应用
程序向导创建应用程序时可以选择不同的视图。
视类
说明
CView
最基本的视类,其他视类都有它派生而来
CScrollView
带滚动条的视类
CCtrlView
基本视类,CListView、CTreeView、CEditView和
CRichEditView都由它派生而来
CListView
显示列表控件的视类
CTreeView
显示树形控件的视类
CFormView
使用对话框资源显示一个格式化窗口的视类
CEditView
提供基本文本编辑功能的视类
CRecordView
能显示数据库记录的视类
CRichEditView
通过使用RichEdit控件提供复杂编辑功能的视类
CDaoRecordView
能显示DAO数据库记录的视类
表3.11
视类及其说明
3.5.1 滚动视图

为 了 实 现 滚 动 视 图 , MFC 提 供 了 一 个 滚 动 视 图 类
CScrollView,该类中常用的成员函数
成员函数
说明
GetTotalSize
获取滚动视图的大小
GetScrollPosit
ion
获取当前可见视图左上角的坐
标
SetScrollSizes
设置整个滚动视图的大小、每
一页和每一行的大小
表3.12
滚动视图类CscrollView的常用成员函数




创建应用程序时一般采用视图类CView作为视图的基类,
如果增加滚动功能,可直接在原来的程序基础上进行修
改:
(1)手工将视图类CView改为CScrollView。
( 2 ) 重 载 视 图 类 虚 函 数 OnInitialUpdate 或
WM_CREATE的消息处理函数 OnCreate。
(3)注意设备坐标与逻辑坐标的转换。

【例3.11】为程序MyDrawLine增加滚动视图的功能。

程序的实现过程如下:

(1)将文件MyDrawLineView.h和MyDrawLineView.cpp
中所有的CView字符串替换为CScrollView字符串。

(2)在类视图中选择类CMyDrawLineView,单击鼠标
右键,在弹出的快捷菜单中选择“添加”菜单项下的
“添加函数”,弹出“添加成员函数向导”对话框。

(3)程序使用的坐标系是MM_TEXT映射模式,原点为
工作区左上角,向右和向下分别为X轴和Y轴的正方向

① 在视图类中定义公有变量ptOrg,用来记录当前视图的
原点坐标

② 修改OnLButtonDown函数,获取当前视图的原点坐标

③ 修改OnLButtonUp函数,更改起点和终点坐标
3.5.2 多视图



文档与视图分离使得一个文档对象可以和多个视图相关
联,这样可以更容易的实现多视图的应用程序。
1.MDI程序的多视图
MFC实现多视图的基本方法如下:
 (1)利用类向导建立新的视图类;
 (2)在函数InitInstance中创建一个与新的视图类相
关联的文档模板对象;在函数ExitInstance中删除创
建的文档模板对象,但暂时不加入它;
( 3 ) 在 相 关 菜 单 的 命 令 处 理 函 数 中 调 用 函 数
CDocTemplate::CreateNewFrame为创建的文档模板
创 建 新 的 框 架 窗 口 , 调 用 函 数
CDocTemplate::InitalUpdateFrame更新视图。

2.SDI程序的多视图
 在SDI程序中可以采用拆分窗口的方式实现多视图。
 拆分窗口主要是通过类CSpitterWnd来实现的。

【例3.13】对【例3.11】进行修改,采用拆分窗口的方法在
单文档应用程序中实现多视图的功能。该程序的功能同【例
3.12】,程序运行的实际效果如图3.25所示。
图3.25
单文档应用程序中的多视图

程序的实现过程如下:

(1)在类视图重生成一个新的视图类CPositionView,其基
类为CScrollView。重写CPositionView类OnInitialUpdate函
数

(2)拆分窗口
① 在框架类CMainFrame的头文件添加共有成员变量
② 在框架类CMainFrame中添加虚函数OnCreateClient,在
该函数中完成拆分窗口的功能



(3)在类CPositionView的OnDraw函数中添加显示文本的
语句