再上一篇:2.8 WM_PAINT消息
上一篇:2.9 WM_DESTROY消息
主页
下一篇:3.2 简单的文本显示
再下一篇:3.3 滚动条的应用
文章列表

3.1 显示的时机

《Windows 下的 C/C++高级编程》讲述Windows环境下调用Win32 API函数程序设计

在字符界面环境下,程序在显示器上输出的内容会一直存在,除非随后的程序有意改写它,所以,程序员不必担心屏幕上的内容会被随意覆盖。也正因此,程序不必为便于将来的恢复而保存当前的屏幕信息。

但是在图形界面的Windows环境下,只能在程序所属的特定窗口内显示文本和图形,并且所显示的内容随时都可能被覆盖而不可见,例如,用户可能移动其他的窗口而覆盖当前窗口的一部分甚至全部。这时,Windows系统本身不会保存被覆盖的区域,它所做的事情只是在覆盖窗口移开、被覆盖的窗口(部分或者全部)需要重新绘制时,通知相应的程序去刷新需要重绘的部分。应当牢记,Windows是一个消息驱动的系统,通过向消息队列添加消息(进队列消息)或者直接向程序中相应的窗口过程发送消息(不进队列消息),通知应用程序所发生的各种事情。在这里,Windows发送WM_PAINT消息,通知应用程序,其相应的窗口用户区的某些部分需要重绘。

3.1.1 发送WM_PAINT消息

WM_PAINT消息是Windows众多消息中极其重要的一个。对于一般的程序而言,自始至终都伴随着WM_PAINT消息的产生、发送、接收和处理。Windows在以下情况发生时发送WM_PAINT消息:

Windows程序在WinMain函数中,进入消息循环之前有一个初始化的过程,绝大多数的应用程序在这里调用UpdateWindow 函数,在这个系统调用中,Windows发送第一个WM_PAINT消息,通知应用程序的窗口过程绘制用户区;

当用户移动窗口或者将最小化的窗口还原时,窗口中原先不可见的部分重新可见;

用户改变窗口大小并且程序设定此时应该更改窗口用户区内容(例如,程序窗口类设置了CS_HREDRAW或CS_VREDRAW风格时);

用户移动滚动条并且程序处理滚动条函数(如ScrollWindow);

程序显式地调用InvalidateRect或InvalidateRgn函数产生WM_PAINT消息。

在某些情况下,窗口用户区的一部分被临时覆盖,Windows会首先试图保存该区域的内 Windows下的C/C++高级编程

容,并在该区域重新可见时自动恢复它。但是,有的时候这种保存工作可能开销很大,此时Windows不会自动保存并恢复,而只是简单地向相应的窗口函数发送一个WM_PAINT消息,交由窗口函数自己去处理。以下是可能发生这种情况的情形:

使用下拉菜单,然后释放;

弹出对话框,然后关闭;

工具栏中图标的提示信息的显示。

在其他一些情况下,Windows总是自动保存被覆盖的区域,然后自动恢复它。列举如下:

鼠标在用户区中移动;

拖动图标经过用户区。

Windows中对显示操作的指导思想是:程序自己应当知道用户区中需要显示的内容,并仅在需要显示或者刷新的时候才进行绘制。程序员需要做的就是设计好正确的窗口函数以响应Windows发送的WM_PAINT消息。

另外,如果需要,程序可以随时强制Windows产生一个WM_PAINT消息,从而实现对用户区的刷新或者绘制。

3.1.2 重绘的范围

当Windows通知窗口函数需要重绘用户区时,并不一定是整个用户区都需要重绘,而很可能只是其中的一部分。例如,当一个覆盖了一部分用户区的对话框被关闭时,只有被对话框覆盖的那部分用户区需要重新绘制(被对话框覆盖的其他窗口区域,例如标题栏、滚动条等的重绘工作交由DefWindowProc函数处理。Windows系统向窗口函数发送一个WM_PAINT消息;窗口函数将该消息交由DefWindowProc函数进行缺省处理;DefWindowProc绘制窗口的非用户区),这部分需要重绘的区域称为“无效矩形区”,如图3-1所示。在任何情况下,Windows通知窗口函数需要重绘的区域总是一个矩形区域。当窗口的用户区中出现无效矩形时,Windows在应用程序的消息队列中放入一个WM_PAINT消息。窗口函数仅在其相应的窗口用户区无效时才会收到WM_PAINT消息。

图3-1 无效矩形区

#第30页~

第3章 文本显示

各种进队列的消息首先在应用程序的消息队列中按优先级排队。WM_PAINT消息的优先级是很低的,通常会有许多的消息因为优先级较高而在WM_PAINT消息之前得到处理。在应用程序处理其他消息的时候,有可能又有新的无效矩形区产生,这样,Windows又需要在消息队列中放入一个WM_PAINT消息。

但是,Windows只为每个应用程序窗口保留一条WM_PAINT消息,以及一个“绘制信息结构体”——PAINTSTRUCT,该结构包含了当前无效矩形区域的坐标以及其他信息。其定义如下:

typedef struct tagPAINTSTRUCT {

HDC hdc; // 显示设备句柄

BOOL fErase; // 是否擦除背景

RECT rcPaint; // 无效矩形区域

BOOL fRestore; // 保留,系统内部使用

BOOL fIncUpdate; // 保留,系统内部使用

BYTE rgbReserved[32]; // 保留,系统内部使用

} PAINTSTRUCT;

其中,参数hdc是一个设备描述句柄,将在下一节详细讲述。参数fErase和rcPaint具体说明如下:

fErase:用于指定应用程序是否需要自己擦除无效矩形区域的背景。如果其值为FALSE(0),则意味着由 Windows 自动擦除背景。Windows 将使用 WNDCLASS 结构中hbrBackground字段指定的刷子来擦除背景。该结构由应用程序在WinMain中注册窗口类时确定,其中定义背景刷的语句如下:

wndclass.hbrBackground = (HBRUSH) GetStockObject ( WHITE_BRUSH );

其中参数WHITE_BRUSH指定所选背景刷为大多数Windows程序所使用的白色刷子。

如果fErase的值为TRUE(任何非零值都意味着TRUE),则由应用程序自己来负责无效区域背景的擦除。在缺省情况下,也是使用hbrBackground所指定的刷子。

如果在注册窗口类的时候未指定hbrBackground(其值为NULL),则必须由应用程序自己负责无效区域背景的擦除,并且不能使用缺省值,而必须在具体应用时显式地指定所使用的背景刷。

rcPaint:rc前缀是rectangle(矩形)的缩写。根据匈牙利表示法,这表明rcPaint是一个矩形结构变量,在Windows中为RECT,其定义如下:

typedef struct _RECT {

LONG left; // 矩形左边框X坐标值

LONG top; // 矩形上边框Y坐标值

LONG right; // 矩形右边框X坐标值

LONG bottom; // 矩形下边框Y坐标值

} RECT;

该结构通过指定左、上、右、下边框的坐标值来定义一个矩形区域,这些坐标值均以像素点为单位。

当某个窗口用户区中出现新的无效矩形区时,Windows在向应用程序的消息队列中放置WM_PAINT消息之前,会首先检查在当前的消息队列中是否已经存在一条准备发送给该应用

#第31页~ Windows下的C/C++高级编程

程序窗口函数的WM_PAINT消息。如果已经有WM_PAINT消息存在,则Windows将这两条WM_PAINT消息合并为一条WM_PAINT消息,并且计算出一个包含原先两个无效区域的新无效矩形区,将这种变化后的信息放入合并后WM_PAINT消息的“绘制信息结构体”中。在任何时候,某个应用程序窗口的消息队列中都不会有多于一个的WM_PAINT消息存在。

程序可以通过调用 GetUpdateRect 系统调用,在任何时候获取无效矩形的坐标。GetUpdateRect函数的定义如下:

BOOL GetUpdateRect(

HWND hWnd, // 窗口句柄

LPRECT lpRect, // 矩形结构体指针,用于接收无效矩形区域

BOOL bErase // 是否擦除背景

);

各参数说明如下:

hWnd:和用户区相关联的窗口句柄;

lpRect:由匈牙利命名法不难看出,该变量是一个指向RECT(矩形)类型变量的指针,用于保存所获得的无效矩形区的坐标值(相对于用户区左上角的坐标)。应用程序可以设置该参数值为NULL,然后调用GetUpdateRect,通过判断返回值是否为非零值来确定是否有无效矩形区产生。应用程序可以通过这种简单有效的方法来判断是否由于出现了无效矩形区而产生了WM_PAINT消息。

bErase:用于指定被刷新的无效矩形区的背景是否需要擦除。如果其值为TRUE(任何非零值即为 TRUE),并且所需刷新的区域不为空,则 GetUpdateRect 函数发送一个WM_ERASEBKGND消息以指定Windows擦除背景。

无效矩形区也是一个所谓的“裁剪矩形区”,意味着Windows系统限制应用程序只能在该矩形区域中绘图。当应用程序使用PAINTSTRUCT结构中的显示设备绘图时,Windows将裁剪掉在rcPaint所标识的矩形区域之外所绘制的图形。

应用程序可以通过调用函数 InvalidateRect 产生一个无效矩形;也可以通过调用函数ValidateRect使一个无效矩形区域变为有效。定义分别为:

BOOL InvalidateRect(

HWND hWnd, // 窗口句柄

CONST RECT *lpRect, // 指向矩形结构体的指针常量,用于指定矩形区域

BOOL bErase // 是否擦除背景

);

hWnd:更改区域所在窗口对象的句柄。如果其值为NULL,则系统将所有的窗口设置为无效并全部重绘,并且在返回之前,向相应的窗口函数发送WM_ERASEBKGND消息(用于擦除背景的相关操作)和WM_NCPAINT消息(用于非用户区绘制的相关操作)。

lpRect:指向矩形结构体(RECT)的指针常量,用于指定变为无效的矩形区域。如果其值为NULL,则整个用户区变为无效。

bErase:用于指定在处理由lpRect所指定的矩形区域时其背景是否需要擦除。如果其值为TRUE(任何非零值),则当BeginPaint函数被调用时,该区域的背景将被擦除,否则其背景保持不便。

#第32页~

第3章 文本显示

BOOL ValidateRect(

HWND hWnd, // 窗口句柄

CONST RECT *lpRect // 指向矩形结构体的指针常量,用于指定矩形区域

);

hWnd:意义与InvalidateRect函数中的hWnd参数相同。

lpRect:指向矩形结构体(RECT)的指针常量,用于指定变为有效的矩形区域。如果其值为NULL,则整个用户区变为有效。

调用ValidateRect函数后,当前应用程序窗口消息队列中的WM_PAINT消息将被删除。