绘图基础
综观历史,Windows被细分成三个主要部分:核心层,处理进程和管理内存;用户层,处理窗口接口和控件;图形设备接口(GDI)负责底层绘制。在Windows CE里,用户层和GDI层合成为图形窗口及事件处理器,即GWE。你可能有时会听Windows CE程序员谈起GWE。GWE并不是什么新事务,只是标准Windows部件的不同包装而已。在本书里,我通常将GWE的图形部分依然称为GDI,以保持和标准Windows编程术语的一致性。
不论你是为Windows CE、2000、还是XP编写程序,需要做的不仅仅是处理WM_PAINT消息这么简单。理解什么时候和为什么WM_PAINT消息要被送到窗口是很有益处的。
有效和无效区域
某些情况下,窗口的一部分暴露给用户,这些部分在Windows里称为区域,这些区域被标记为无效。当应用程序消息队列里没有其它消息在等待处理并且应用程序窗口包含有无效区域的时候,Windows会给窗口发送WM_PAINT消息。正如第一章里提到的,处理WM_PAINT消息的绘制操作是包含在BeginPaint和EndPaint调用之间的。BeginPaint实际上执行很多动作。首先将无效区域标记为有效,接下来计算需要裁减的区域。裁减区是限制绘图动作的区域。BeginPaint接下来发送WM_ERASEBACKGROUND消息,如果需要,还会重绘背景,如果用于指示文本输入的光标可见,它还会隐藏光标。最后BeginPaint获得显示设备环境变量的句柄,该句柄可以用于应用程序中。EndPaint函数则释放设备环境,如果必要,还会重新显示光标。如果WM_PAINT处理过程没有更多的操作要执行,您也必须至少调用BeginPaint 和EndPaint ,以便将无效区域标记为有效。
作为替代,您可以调用ValidateRect来强制使矩形有效。但这种情况下没有绘制动作发生,因为应用程序在窗口上绘制任何东西之前,必须有设备环境句柄。
应用程序经常需要强制重画它的窗口。应用程序决不应该邮递或发送(post or send)WM_PAINT消息给自身或其它窗口。您应该使用以下函数:
BOOL InvalidateRect (HWND hWnd, const RECT *lpRect, BOOL bErase);
请注意InvalidateRect并不要求窗口设备环境句柄,只要窗口句柄自身。lpRect表示窗口中需要无效的区域。该参数为NULL则表示整个窗口无效。bErase用来指出在调用BeginPaint的时候是否重画窗口背景。请注意不像Windows的其它版本,Windows CE要求hWnd必须是一个有效的窗口句柄。
设备环境
设备环境经常被简称为DC,它被Windows用于管理对显示器和打印机的访问,当然在本章我将只讨论显示器。除非特别说明,下面的讨论适合所有Windows,而并不具体针对Windows CE。
Windows应用程序从不直接写屏幕。相反,它们为适当的窗口请求一个显示设备环境句柄,之后用该句柄在设备环境里绘制。接下来Windows作为中间人将像素从DC操纵到屏幕上。
只应该在WM_PAINT消息里调用BeginPaint,它为窗口返回一个显示DC句柄。通常应用程序在处理WM_PAINT消息时在屏幕上绘制东西。Windows将绘制作为一个低优先级的任务,这样做是适当的,因为将绘制作为高优先级会导致为每个小的显示改变都产生一个绘图消息,这将使绘图消息泛滥。而通过处理所有正在等待的消息,允许应用程序先完成所有未完成的事务,这样使所有无效区域能够被有效的一次性绘制完成。用户并不会注意到由WM_PAINT消息低优先级所带来的微小延迟。
当然,有些时候是需要立即绘制的。字处理器就是一个例子,当键盘被按下后,字处理器需要立即显示对应的字符。为了在WM_PAINT消息以外的时刻进行绘制,可以用GetDC函数来获得DC句柄。GetDC原型如下:HDC GetDC (HWND hWnd);
GetDC返回窗口客户区DC句柄。接下来可以在窗口客户区的任何地方进行绘制,因为这个过程不像WM_PAINT消息的处理过程,没有裁减区域来限制您只能在一个无效区域里绘制。
Windows CE支持另一个获得DC的函数,该函数如下:
HDC GetDCEx (HWND hWnd, HRGN hrgnClip, DWORD flags);
GetDCEx允许您对返回的设备环境有更多的控制。新参数hrgnClip使您可以定义用来限制绘制区域的裁减区域。flags指出当您在DC上绘制的时候,DC如何反应。注意Windows CE不支持下面的标志:DCX_PARENTCLIP, DCX_NORESETATTRS, DCX_LOCKWINDOWUPDATE, 和DCX_VALIDATE。
当绘制完成后,必须调用ReleaseDC来释放设备环境。ReleaseDC原型如下:
int ReleaseDC (HWND hWnd, HDC hDC);
GetDC用于在客户区内绘制,有时应用程序需要访问窗口非客户区,比如标题栏。为了获得整个窗口的DC,使用以下函数:HDC GetWindowDC (HWND hWnd);
如前所述,当绘制完成后,要调用ReleaseDC。
Windows CE下的DC函数同XP下的设备环境函数是一样的。这是可以预料到的,因为DC是Windows 绘图体系里的核心。对这个领域的API进行改变将导致Windows CE程序与它的桌面程序严重的不兼容。