2.2、绘制和重画用户区 你试着运行上节给出的程序。当该应用程序的窗口出现在屏幕上时,观察窗口中显示的信息。然后使用鼠标(或键盘)移动该窗口;将窗口隐藏在其它窗口后面,再让它显现出来;使窗口以最小化方式显示,再恢复为原窗口大小。在对该窗口对象进行上述操作之后,观察用户区显示的信息有什么变化。 上述操作并没有改变(也未破坏)用户区中显示的信息,为什么呢?要回答这个问题,必须首先明白消息WM_PAINT的作用。 Windows是一种消息驱动的系统。通过把消息邮寄到应用程序的队列中(甚至于直接发送到有关的窗口对象上),Windows通知应用程序所发生的各种有关事件。当Windows认为(由于窗口用户区的某些部分显示的内容受到破坏)需要窗口对象重新绘制用户区时,Windows就在应用程序的消息队列中放置一条WM_PAINT消息,窗口对象在接受到WM_PAINT消息之后,就重新绘制它的用户区中所显示的内容。 在前面一章我们曾介绍了Windows应用程序的面向对象的特点,面向对象的方法将对象视为能作出动作。因此,当一个对象(例如Windows)需要另一个对象作某件事时,就向它发送相应的消息,让它动作起来。绘制对象的用户区是对象自己的事,所以,当一个对象的用户区需要重绘时,Windows就向这个对象发送WM_PAINT消息。Windows知道在什么情况下需要窗口对象重绘其用户区。
2.3、有效和无效矩形区 当Windows通知窗口对象重绘用户区时,并非整个用户区都要重绘。例如,当一个覆盖了窗口用户区域的对话框消失时,只有被对话框覆盖的用户区需要重新绘制(被对话框覆盖的其它区域,例如标题栏、滚动杠等重绘问题由函数DefWindowProc负责。Windows向窗口对象发送WM_NCPAINT消息;窗口对象将这个消息交给DefWindowProc函数进行缺省处理;DefWindowProc绘制窗口对象的非用户区)。这个需要重绘的区域称为“无效矩形区”。如图2-1所示。在任何情况下,Windows通知窗口对象需要重绘的区域总是一个矩形区域。在用户区中出现的一个无效矩形提示Windows在应用程序的消息队列中放置WM_PAINT消息,窗口对象仅在其用户区无效接收到WM_PAINT消息。
 | 图2-1 窗口用户区的无效矩形区 |
各种排队的消息首先在应用程序的消息队列中按优先级排队,WM_PAINT有最低的优先别,它总是在队列中的其它消息都被处理完之后才被处理。因此,在应用程序处理其他消息时,有可能又出现新的无效矩形,这样Windows又要向消息队列中放置WM_PAINT消息。 但是,Windows只为每个窗口对象保留一条WM_PAINT消息和一个“绘制信息结果” — PAINTSTRUCT,这个结构内包含有无效矩形区的坐标。当一个窗口对象出现新的无效矩形区时,Windows在向应用程序的消息队列中放置WM_PAINT消息之前,它首先检查在消息队列中是否已存在一条准备发送给该窗口对象的WM_PAINT消息。若该消息存在,Windows将这两条WM_PAINT消息合并为一条WM_PAINT消息。该消息的“绘制信息结构”的无效矩形区将包含原来的两个WM_PAINT消息的无效矩形区。 在窗口对象处理WM_PAINT消息时,通过函数BeginPaint可以获得无效矩形区的坐标。在其他情况下,只能通过调用函数GetUpdateRect()获得无效矩形区的坐标(见表2-2)
表2-2 GetUpdateRect 函数
用 途 | 检索无效矩形区的坐标,如果该矩为空,则所有坐标值为零。 | 原 型 | GetUpdateRect( | | HWND hWnd, | 和用户区相关联的窗口对象的句柄 | LPRECT lpRect, | 指向类型为RECT的变量的指针,该变量用于保存所获得的无效矩形区的坐标(相对于用户区左上角的坐标) | BOOL bErase | 所要绘制的字符个数 | ); | |
| |
返回值 | 如果更新区不空,返回非零,否则返回零。 |
无效矩形也是一个裁剪矩形,也就是说,Windows限制应用程序只能在这个区域中绘图。当使用PAINSTRUCT结构中的显示设备对象绘图时,Windows将裁剪掉在rcPaint域所标识的矩形之外所绘的图。 应用程序可以使用函数InvalidateRect()产生一个的无效矩形。表2-3给出了函数InvalidateRect()和函数ValidateRect()的使用说明。
表2-3-1 InvalidateRect() 函数
用 途 | 将该函数所指定的一个矩形作为无效矩形区加到窗口的无效矩形区上。 | 原 型 | VOID InvalidateRect( | | HWND hWnd, | 和无效矩形区相关联的窗口的句柄 | LPRECT lpRect, | 指向RECT类型的变量的指针,该变量定义的矩形被加到无效矩形区上,如果该指针为NULL,则整个用户区变为无效矩形区 | BOOL bErase | 是(非零)否(零)擦除lpRect所标识的区域的内容 | ); | |
| |
注 释 | 当该函数被调用时,它生成一条WM_PAINT消息,该消息所附带的无效矩形区由lpRect指定。Windows将该消息和应用程序信息队列中已存在的一条WM_PAINT消息合并。 |
表2-3-2 ValidateRect() 函数
用 途 | 使无效矩形区有效。 | 原 型 | VOID ValidateRect( | | HWND hWnd, | 窗口句柄 | LPRECT lpRect, | 指向RECT类型的变量的指针,无效矩形区将不再包含由该变量所指定的矩形区域,如果其值为NULL,则整个用户区有效 | ); | |
| |
注 释 | 函数BeginPaint()自动使整个用户区有效。 |
进行裁剪操作是费时间的,为了提高程序的运行效率,当用户区显示的内容不很复杂时(或可以很有效地重画整个用户区时),可以在调用BeginPaint()函数之前调用InvalidateRect函数使整个用户区无效,然后,应用程序在整个用户区上进行绘制。 由于WM_PAINT消息的优先级很低,这样,由于窗口对象不能及时收到WM_PAINT消息而影响用户对屏幕对象的视觉感觉。为弥补这个缺陷,程序员可以考虑使用函数UpdateWindows(),它在应用程序的消息队列中存在WM_PAINT消息的情况下,强使Windows立即向窗口对象发送WM_PAINT消息。例如,前面几个示例程序的WinMain函数中,我们多次使用了这个函数。这样,程序一启动,用户可以很快在屏幕上看到窗口。 EndPaint不仅归还显示设备对象,同时,它还清除应用程序的消息队列中的WM_PAINT消息。当使用函数ValidateRect使用窗口对象不存在任何无效的矩形区域时,ValidateRect函数同样也清除消息队列中的WM_PAINT消息。
2.4、格式化显示信息 本节介绍另一个绘制字符的函数DrawText(),表2-4给出了该函数的使用说明。
// 2-4-1.c LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hDC; PAINTSTRUCT ps; RECT rect;
switch(message) { case WM_PAINT: hDC = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); DrawText(hDC, "Hello, Welcome to Windows!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); return 0L;
case WM_DESTROY: PostQuitMessage(0); return 0L; } return DefWindowProc(hwnd, message, wParam, lParam); }
该程序在用户区的中心显示一条信息,用户试着移动屏幕上窗口的位置,或改变窗口的大小,信息始终显示在中心位置。该程序有助于你更好地理解WM_PAINT消息的作用。 程序中使用了函数GetClientRect(),它将用户区的坐标(左上角和右下角,其中左上角坐标总为(0,0),而右下角坐标是相对于用户区左上角坐标)拷贝到第二个指针参数所向的RECT类型的变量。
 
2/2 首页 上一页 1 2 |