再上一篇:14.4 文件流
上一篇:第十五章 MFC程序设计基础
主页
下一篇:15.3 MFC的数组类
再下一篇:15.4 鼠标使用实例
文章列表

15.2 文档与视图结构

《VC++程序设计基础》,讲述C++语言的语法和标准库,以及Visual C++ 函数库和MFC类库的使用,并附相关代码示例。

主边框窗口、文档、视图是MFC应用程序的最主要的对象,只有弄清这三者之间的关系,才能在应用程序的框架结构上根据程序设计的需要修改程序代码。通常,文档对象管理应用程序的数据,文档类中的程序代码要完成定义和操纵应用程序的数据;视图对象显示文档数据,管理文档与用户之间的交互,视图类中的程序代码要完成数据的显示,并解释用户输入;主边框窗口对象包含并管理视图。

15.2.1 窗口

任一个窗口可分为两部分:一是窗口的边框,另一个是边框里面的内容。WINDOWS系统自动管理窗口的移动、改变窗口的大小、关闭窗口、窗口最大化/最小化等工作,程序设计者只要完成管理边框里面的内容。

在MFC中,窗口的边框由边框窗口类负责管理,而窗口中的内容由视图类负责管理。视图窗口是边框窗口的子窗口,用户与文档间的交互以及所有的绘制工作都发生在视图窗口的客户区内。边框窗口在视图窗口周围提供一个可见的边框、一个标题栏、菜单条、控制按钮,边框窗口的内容形成窗口的客户区,并被其子窗口(视图窗口)完全占有。

每一个应用程序有一个主边框窗口,主边框窗口通常含有标题栏、菜单条。每一个文档都有一个对应的文档边框窗口,它是主边框窗口的子窗口,一个文档边框窗口至少含有一个相关联的视图窗口,该视图窗口用来显示文档数据。

SDI应用程序有一个从CFrameWnd派生的边框窗口,它既是主边框窗口,又是文档的边框窗口,其客户区是视图窗口。MDI应用程序有一个从CMDIFrameWnd派生的主边框窗口,而文档的边框窗口是主边框窗口的子窗口,由 CMDIChildWnd派生而来。视图窗口是文档边框窗口的子窗口,文档窗口的客户区是视图窗口。

边框窗口管理菜单、工具条、状态栏等用户界面对象的更新。边框窗口可以跟踪当前活动的视图,对于SDI边框窗口只有一个视图;而对于MDI,边框窗口含有多个视图。当前视图就是最近使用的视图,只有当前视图才能接收用户的键盘或鼠标操作。

15.2.2 文档与视图结构

文档对象的主要任务是管理应用程序的数据。通常按如下方法使用文档类:

(1)从CDocument类派生出满足应用程序需求的各种不同类型的文档类,每一种类型对应于一种文档;

(2)在文档类中添加用于存放文档数据的数据成员;

(3)在文档类中重载CDocument的成员函数Serialize,实现文档数据的文件操作(从文件中读取数据或将数据写入文件中);

(4)根据应用程序的需要,可以重载CDocument的其它成员函数或增加新的成员函数。

视图对象可通过GetDocument函数来获取指向相关联文档对象的指针,并通过该指针来访问文档的数据成员和成员函数。

视图对象以图形方式显示文档数据,接收用户的键盘或鼠标输入,并将用户的输入解释为对文档的操作。通常按如下方法使用视图类:

(1)修改成员函数 OnDraw,通过指向相关联文档的指针来获取文档对象中的数据,并将数据送视图窗口中显示;

(2)将WINDOWS消息与用户界面联系起来,将用户输入事件与视图类中的消息处理函数联系起来;

(3)修改相关的消息处理函数,解释用户输入事件;

(4)在CView的派生类中重载CView的其它成员函数或增加新的成员函数,如重载初始化成员函数、更新成员函数等,以满足应用程序的需求。

在MFC中,窗口、文档、视图、应用程序对象之间具有以下关系:文档对象中含有与其关联的各视图对象的列表,有指向文档模板对象的指针;视图对象中含有指向相关联文档对象的指针,视图窗口是文档边框窗口的子窗口;文档边框窗口含有指向当前活动视图对象的指针;应用程序对象中含有文档模板对象的一个列表;文档模板对象含有已打开文档对象的一个列表;Windiows管理所有已打开的窗口,并发送消息给这些窗口;在同一应用程序中的任何对象中,可通过全局函数AfxGetApp获得指向应用程序对象的指针。这些关系是在文档和视图对象的创建过程中建立起来的。一个对象要访问另一个对象中的成员时,通过调用相关的函数来实现。表15-1给出了源对象访问目的对象的方式。

表15-1 MFC各对象之间的访问方式

源对象 目的对象 调用的函数 说明

文档 视图 GetFirstViewPosition 获取第一个视图的位置文档 视图 GetNextView 获取下一个视图的位置 文档 文档模板对象 GetDocTemplate 获取文档模板对象的指针视图 文档对象 GetDocument 获取文档对象的指针视图 文档边框窗口对象 GetParentFrame 获取文档边框窗口对象的指

文档边框窗口 当前视图对象 GetActiveView 获取当前视图对象文档边框窗口 当前文档对象 GetActiveDocument 获取当前活动的文档对象MDI主边框窗口 当前活动的MDI子 MDIGetActive 获取当前活动的MDI子窗口

窗口

15.2.3 消息与命令的处理

事件驱动是指每当发生一个事件时,系统产生一个消息,并将该消息发送给处理该消息的窗口对象。若该窗口对象中已定义了该消息的处理函数,则由系统自动调用该函数来处理该消息。每一个消息有一个消息标识(一个无符号整数,用宏名来表示),使用类向导建立起消息标识与消息处理函数间的映射关系。通常还要用一个数据来表示消息的内容,这部分内容随消息不同而变化,并将消息数据作为参数传递给消息处理函数。

MFC编程的主要任务之一是设计消息处理函数。通常消息处理函数是通过ClassWizard来创建的,并产生消息处理函数的框架,程序设计者的任务是根据应用程序的需求在消息处理函数的框架内,增加相应的程序代码。

MFC中将消息分为三类:标准WINDOWS消息、控件通知和命令消息。

15.2.3.1 标准WINDOWS消息

除WM_COMMAND以外,所有以WM_开头的消息都是标准的WINDOWS消息。这类消息具有两个特点:首先必须由窗口和视图对象处理这类消息;其次,均有缺省的消息处理函数,在CWnd类中预定义了缺省的消息处理函数(见头文件afxwin.h)。

MFC类库中对消息的处理有统一的格式,例如,字符消息的处理函数的原型为:

afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);

消息处理函数名均由“On”和消息名“Char”组成。关键字afx_msg用于区分消息处理函数与其它的成员函数。消息处理函数是通过消息映射来实现的,而消息映射依赖于标准的编译预处理宏,而与C++的编译器无关。经编译预处理后,将删除关键字afx_msg。

应用程序要处理的标准WINDOWS消息有:字符输入消息,鼠标消息、重画消息、滚动消息等。下面分别加以介绍。

(1) 字符消息WM_CHAR

每当按下键盘上的一个键时,产生一个WM_CHAR消息,处理该消息的成员函数的格式为:

afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);

其中nChar为所按下字符的代码值(ASCII码),nRepCnt为重复次数(通常为1),nFlags表示扫描码、先前键状态、转换状态等,其含义见表15-2。

表15-2 nFlags各字段的含义

位 含义

0~7 扫描码

8 若同时按下扩展键(功能键或小键盘上的键)为1,否则为0

9~10 未用

11~12 WINDOWS内部使用

13 若同时按下Alt键则为1,否则为0

14 先前键的状态,若消息发送前键处于按下状态,则为1,否则为0

15 指明键转换状态,若键已松开,则为1,否则为0

实际上,当按下键盘上某一数字或字符键时,系统产生WM_KEYDOWN消息;松开一个键时产生WM_KEYUP消息;这二个消息经组合后产生WM_CHAR消息;当按下Alt键时产生WM_SYSTEMDOWN消息;松开Alt键时产生WM_SYSTEMUP消息。可为每一个消息建立一个消息处理函数。

(2) 鼠标消息

鼠标消息有:WM_LBUTTONDOWN(按下鼠标左键),WM_LBUTTONUP(松开鼠标左键),WM_RBUTTONDOWN(按下鼠标右键),WM_RBUTTONUP(松开鼠标右键),WM_MOUSEMOVE(拖动鼠标),WM_LBUTTONDBLCLK(双击左键)。处理鼠标消息的成员函数的原型类同,例如,处理鼠标左键弹起的消息处理函数的原型为:

afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

其中,CPoint的定义为:

typedef struct targCPoint{

short x,y;

}CPoint;

即鼠标事件发生时,point给出了鼠标在窗口中的坐标(x,y),坐标原点是窗口的左上角。nFlags中的一位表示一个状态,其值与含义见表15-3。例如,nFlags & MK_LBUTTON的值为 1时,表示按下鼠标左键。

表15-3 nFlags中位屏蔽的含义

位屏蔽 含义

MK_CONTROL 按下Ctrl键为1,否则为0

MK_SHIFT 按下Shift键为1,否则为0

MK_LBUTTON 按下鼠标左键为1,否则为0

MK_MBUTTON 按下鼠标中键为1,否则为0

MK_RBUTTON 按下鼠标右键为1,否则为0

(3) 重画消息WM_PAINT

当窗口的大小发生变化、窗口内容发生变化、窗口间的层迭关系发生变化或调用成员函数UpdateWindow或RedrawWindow时,系统将产生WM_PAINT消息,表示要重新绘制窗口的内容。该消息处理函数的原型为:

afx_msg void OnPaint( );

该函数的处理过程为:首先调用BeginPaint函数,将窗口更新的区域置为NULL,封锁接着的WM_PAINT消息;然后,根据文档类中的数据绘制图形;最后,调用EndPaint函数结束绘制过程。

(4) 滚动消息WM_HSCROLL和WM_VSCROLL

水平滚动的消息为WM_HSCROLL,垂直滚动消息为WM_VSCROLL;消息处理函数的原型为:

afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);

afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);

其中nSBCode用于区分向左(上)或向右(下)滚动一行、向左(上)或向右(下)滚动一页;nPos仅当要滚动到指定的位置时,其值才有意义(当前位置);pScrollBar指向滚动条控件。

15.2.3.2 控件通知消息

控件通知消息属于命令消息中的一类,包括控件产生的消息和子窗口传送给父窗口的命令消息WM_COMMAND。例如,当用户改变编辑控件的中文本时,它向父窗口发送一条已改变文本内容的控件通知消息。用户单击按钮控件时,是作为命令消息来处理的,而不是作为控件通知消息来处理的。控件通知消息由窗口和视图对象来处理。

15.2.3.3 命令消息

用户选择菜单项、工具栏按钮、单击按钮时所产生的消息称为命令消息。每一个命令消息都要定义一个命令ID,MFC类库已预定义了某些命令的ID(见afxres.h),一般命令的ID由编程人员自行定义,定义的方法在后面的例子中给出。

注意,命令消息都没有缺省的消息处理函数,这类消息处理函数都没有参数,也没有返回值。

在MFC中,消息的发送与接收过程为:每当产生一个消息时,由CWinApp的成员函数Run检索到该消息,并将该消息发送给相应的窗口对象,经消息映射后自动调用相匹配的消息处理函数。

在 MFC 中,消息映射是通过宏来实现。为了标识这种特殊的映射,用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP把消息映射括起来。消息映射的格式为:

ON_COMMAND(ID, FunName)

其中第一个参数为消息标识,第二个参数为处理该消息的函数名,即要在该消息与消息处理函数名之间建立起映射关系。例如,工程文件Test1中建立的消息映射为:

BEGIN_MESSAGE_MAP(CCh1001App, CWinApp)

//{{AFX_MSG_MAP(CCh1001App)

ON_COMMAND(ID_APP_ABOUT, OnAppAbout)

// NOTE - the ClassWizard will add and remove mapping macros here.

// DO NOT EDIT what you see in these blocks of generated code!

//}}AFX_MSG_MAP

// Standard file based document commands

ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)

ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)

// Standard print setup command

ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)

END_MESSAGE_MAP()

注意,在消息映射的宏后,没有分号;这部分内容不能随便修改;对于消息映射中的

//{{AFX_MSG_MAP(CCh1001App)和//}}AFX_MSG_MAP 是将映射条目括起来,ClassWizard要用到这种标记,绝不能删除这种标记。表15-4中给出了在MFC中消息映射的宏。表中ON_WM_XXXX表示以ON_WM_开头所有消息名,ON_XXXX的含义类同。

表15-4 消息映射宏

宏格式 消息类型

ON_WM_XXXX 预定义的WINDOWS消息

ON_COMMAND 命令

ON_UPDATE_COMMAND_UI 更新命令

ON_XXXX 控件通知

ON_MESSAGE 用户自己定义的消息

ON_REGISTERED_MESSAGE 已注册的WINDOWS消息

ON_COMMAND_RANGE 命令ID范围(处理指定范围内的命令)ON_ UPDATE_ COMMAND_RANGE 更新命令ID范围

ON_CONTROL_RANGE 控件ID范围