更新时间:2023-05-04 20:44
WndProc处理的第二个消息为WM_PAINT。这个消息在Windows程序设计中是很重要的。当窗口显示区域的一部分显示内容或者全部变为“无效”,以致于必须“更新画面”时,将由这个消息通知程序。
显示区域的显示内容怎么会变得无效呢?在最初建立窗口的时候,整个显示区域都是无效的,因为程序还没有在窗口上画什么东西。第一条WM_PAINT消息(通常发生在WinMain中调用UpdateWindow时)指示窗口消息处理程序在显示区域上画一些东西。在用户改变HELLOWIN窗口的大小后,显示区域的显示内容重新变得无效。wndclass结构的style字段设定为标志CS_HREDRAW和CS_VREDRAW,这样的格式设定指示Windows,在窗口大小改变后,就把整个窗口显示内容当成无效。然后,窗口消息处理程序将收到一条WM_PAINT消息。当用户将最小化,然后再次将窗口恢复为以前的大小时,Windows将不会保存显示区域的内容。在图形环境下,窗口显示区域涉及的数据量很大。因此,Windows令窗口无效,窗口消息处理程序接收一条WM_PAINT消息,并自动恢复其窗口的内容。在移动窗口以致其相互重叠时,Windows不保存一个窗口中被另一个窗口所遮盖的内容。在这一部分不再被遮盖之后,它就被标志为无效。窗口消息处理程序接收到一条WM_PAINT消息,以更新窗口的内容。
当需要绘制一部分应用窗口的时候,这个消息被Windows或者其他应用程序绘制调用。这个消息一般在调用UpdateWindow函数或者当应用程序通过GetMessage函数或者PeekMessage函数来获得一个WM_PAINT消息时候被DispatchMessage函数时候被发送。
WM_PAINT hdc=(HDC) wParam;
对WM_PAINT的处理几乎总是从一个BeginPaint调用开始:hdc = BeginPaint (hwnd, &ps) ;而以一个EndPaint调用结束:EndPaint (hwnd, &ps) ;
在这两个调用中,第一个参数都是程序的窗口句柄,第二个参数是指向型态为PAINTSTRUCT的结构指针。PAINTSTRUCT结构中包含一些窗口消息处理程序,可以用来更新显示区域的内容。我们将在下一章中讨论该结构的各个字段。这里我们只在BeginPaint和EndPaint函数中用到它。
在BeginPaint调用中,如果显示区域的背景还未被删除,则由Windows来删除。它使用注册窗口类别的WNDCLASS结构的hbrBackground字段中指定的画刷来删除背景。一般, 这是一个白色备用画刷。这意味着,Windows将通过把窗口背景设定为白色来删除窗口背景。BeginPaint调用令整个显示区域有效,并传回一个“设备上下文句柄”。设备上下文是指实体输出设备(如视频显示器)及其设备驱动程序。在窗口的显示区域显示文字和图形需要设备上下文句柄。但是从BeginPaint传回的设备上下文句柄不能在显示区域之外绘图,读者可以试一试。EndPaint释放设备上下文句柄,使之不再有效。
如果窗口消息处理程序不处理WM_PAINT消息(这是很少见的),它们必须被传送给DefWindowProc。DefWindowProc只是依次调用BeginPaint和EndPaint,以使显示区域有效。调用完BeginPaint之后,WndProc接着调用GetClientRect:
GetClientRect (hwnd, &rect) ;
第一个参数是程序窗口的句柄。第二个参数是一个指针,指向一个RECT型态的rectangle结构。该结构有四个LONG字段,分别为left、top、right和bottom。GetClientRect将这四个字段设定为窗口显示区域的尺寸。left和top字段通常设定为0,right和bottom字段设定为显示区域的宽度和高度(像素点数)。WndProc除了将该RECT结构指针作为DrawText的第四个参数传递外,不再对它做其它处理
系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽可能地推后做。不过这样也有利于提高绘制的效率:两个WM_PAINT消息之间通过InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到 更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统。在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送WM_PAINT消息而不管Update Region是否为空等。
BeginPaint和WM_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写BeginPaint会怎样?程序会像进入了一个死循环一样达到惊人的CPU占用率,你会发现程序总在处理一个接 一个的WM_PAINT消息。这是因为在通常情况下,当应用收到WM_PAINT消息时,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了),BeginPaint的一个作用就是把该Update Region置为空,这样如果不调用BeginPaint,窗口的Update Region就一直不为空,如前所述,系统就会一直发送WM_PAINT消息。