Programming Windows 笔记:Text Output(四)
本文为《Programming Windows, Fifth Edition》(Charles Petzold 著)第4章 “Text Output” 的学习笔记总结。
WM_PAINT 消息
大多数 Windows 程序在 WinMain
函数初始化过程中会在进入消息循环之前调用 UpdateWindow
函数。 Windows 利用这个机会向窗口过程发送最初的 WM_PAINT
消息。这个消息通知窗口过程绘制客户区。此后,窗口过程需要在几乎任何时间都能够处理新收到的
WM_PAINT
消息,而且能够在必要时重绘整个客户区。在以下任何一个事件发生时,窗口过程都会收到一条
WM_PAINT 消息:
- 用户移动一个窗口,导致原来被遮盖的部分窗口暴露出来。
- 用户调整窗口的大小(当窗口的类型设定为
CS_HREDRAW和CS_VREDRAW值时)。 - 程序调用
ScrollWindow或ScrollDC函数滚动客户区。 - 程序调用
InvalidateRect或InvalidateRgn函数显式生成WM_PAINT消息。
处理 WM_PAINT
消息需要你改变对于视频输出的概念。你的程序应该收集并保存所有用于绘制客户区的信息,但是只在需要时才进行绘制——当收到
WM_PAINT 消息时。如果程序需要在其他时候更新客户区,可以强制
Windows 生成 WM_PAINT
消息。这看上去貌似绕了一个弯,但是程序的结构会因此而受益。
获取设备环境句柄
Windows 应用程序需要在屏幕上绘图时,通常有两种方法获取设备环境句柄。
方法一:BeginPaint 和 EndPaint
通常,处理 WM PAINT 消息的代码如下所示:
1 | case WM_PAINT: |
hwnd
是窗口的句柄,这是窗口消息处理过程的参数,ps 是一个类型为
PAINTSTRUCT 结构的变量的地址。 函数的返回值
hdc 就是设备环境句柄。
在处理 WM_PAINT 消息时,窗口过程首先调用
BeginPaint
函数。这个函数通常会擦去无效区域的背景以便绘图。它同时还会填充
ps 结构的各个字段。
绘制信息结构 PAINTSTRUCT
Windows 将为每个窗口维护一个绘制信息结构
PAINTSTRUCT。定义如下:
1 | typedef struct tagPAINTSTRUCT { // ps |
当程序调用 BeginPaint 函数时, Windows
将自动填充这个结构中的字段。程序只能够使用前三个字段,其他的供 Windows
内部使用。 hdc 字段是设备环境句柄。 BeginPaint
函数的返回值就是这个设备环境句柄的值(这里表现了 Windows
典型的冗余性)。在大多数情况下, fFrase 字段将被设置为
FALSE。这意味着 Windows 在先前的 BeginPaint
函数中己经擦除了无效区域的背景。(如果想在窗口过程中自定义背景擦除方式,必须自己处理
WM_ERASEBKGND 消息。)在 WINMAIN
初始化时,用于注册窗口类的 WNDCLASS 结构中的
hbrBackground 字段指定了一个画刷, Windows
就使用这个画刷来擦除背景。
但是,当你的程序通过调用 InvalidateRect
函数使客户区中的一个矩形无效时, InvalidateRect
函数的最后一个参数将指定背景是否要被擦除。如果参数是
FALSE,Windows 将不会擦除背景,同时在调用
BeginPaint 函数后, PAINTSTRUCT 结构的
fErase 字段的值将是 TRUE。
即如果 InvalidateRect
函数的最后一个参数为0,不擦除背景,fErase
填充为1。函数的最后一个参数为1,擦除背景,fErase
填充为0。
结构中的 rcPaint
矩形不仅仅是无效矩形,而且还是一个“裁剪”(Clipping) 矩形。这意味着
Windows 将把绘制限制在这个矩形里。更确切地说,如果无效区域不是一个矩形,
Windows 将把绘制限制在这个区域内。
方法二:GetDC 和 ReleaseDC
尽管最好让程序在处理 WM_PAINT
消息时才更新整个客户区,但有时也会发现在处理非 WM_PAINT
消息时绘制部分客户区也是很有用的。有些时候,还需要设备环境句柄用作其他用途,例如获取设备环境的信息。
1 | hdc = GetDC(hwnd); |
GetDC
返回的设备环境句枘中的裁剪矩形是整个客户区。这意味着你可以在客户区的任意部分绘制,而不仅仅是在无效矩形里,也就是说如果不存在无效矩形也没有关系。与
BeginPaint 不同, GetDC
不会将无效区域有效化。如果需要将整个客户区有效化,可以像下面这样调用函数:
1 | VaIidateRect (hwnd, NULL); |
通常, GetDC 和 ReleaseDC
函数用于处理键盘消息(例如在字处理程序中)或者鼠标消息(例如绘图程序中)。使用这两个函数,程序可以在收到用户的鍵盘或鼠标输入时及时地绘制客户区,而不必为了生成
WM_PAINT
消息去刻意使客户区的一部分无效化。但是,即使程序在处理非
WM_PAINT
消息时进行了绘制,它仍然必须收集足够的信息以便在收到
WM_PAINT 时能更新显示。