首页 理论教育VC++MFC绘图专用句柄HDC

VC++MFC绘图专用句柄HDC

【摘要】:HWND专门用于窗口操作,被MFC封装于CWnd类中;HDC是专门用于绘图的句柄,被MFC封装于CDC类中。创建一个工程名为“DC32”的Win32程序,演示通过HDC句柄进行绘图的三种方式。本示例演示的是客户区标准绘图方式,通过绘图句柄在客户区内绘制了2个矩形和1条线。在任何消息回调时都可以执行,调用GetDC获取临时客户区绘图句柄进行绘图,最后调用ReleaseDC释放。GetDC函数返回的设备环境也是基于客户区坐标系的,该函数可以在任何消息中使用。

在第2章Windows数据类型列表中就介绍过,HDC和HWND一样是Windows中最常用的句柄之一。HWND(Handle of Window,窗口句柄)专门用于窗口操作,被MFC封装于CWnd类中;HDC(Handle of DC,设备环境句柄)是专门用于绘图的句柄,被MFC封装于CDC类中。

DC(Device Context)一般被称为设备上下文、设备环境或设备描述表。早期开发图形程序都是直接针对具体设备进行的,要开发一个图形软件必须先了解是什么型号的显示卡或者打印机,根据每个厂家提供不同的接口编写不同的代码来开发。进入Windows时代,操作系统通过对驱动程序的统一管理,将设备接口细节隐藏于操作系统内部。程序员在编写图形程序时,只要调用一个公用的虚拟设备即可,这个虚拟设备环境也就是DC。

通过HDC句柄绘图有三种方式,即标准客户区绘图、临时客户区绘图和非客户区绘图。

创建一个工程名为“DC32”的Win32程序,演示通过HDC句柄进行绘图的三种方式。

1)执行File→New命令,或者按快捷键<Ctrl+N>弹出新建对话框,如图7-1所示。

选中“Win32 Application”列表项,先在“Location”文本框中选择一个目录再填写工程名“DC32”。

2)单击“OK”按钮后进入Win32应用程序向导,如图7-2所示。

选中“A simple Win32 application”单选按钮,并单击“Finish”按钮完成工程创建。

978-7-111-46378-8-Chapter07-1.jpg

图7-1 新建Win32工程

978-7-111-46378-8-Chapter07-2.jpg

图7-2 Win32应用程序向导

3)创建完工程后再执行File→New命令,或按快捷键<Ctrl+N>弹出对话框,如图7-3所示。

978-7-111-46378-8-Chapter07-3.jpg

图7-3 为Win32工程添加资源

在Files分页中选中“Resource Script”列表项,在“File”文本框中输入名称后单击“OK”按钮。

4)在新加入工作区的资源视图(ResourceView)中,通过菜单命令或按快捷键<Ctrl+R>添加资源,如图7-4所示。

5)在插入资源对话框中,选中“Dialog”项再单击“New”按钮,如图7-5所示。

978-7-111-46378-8-Chapter07-4.jpg

图7-4 通过菜单命令插入资源

978-7-111-46378-8-Chapter07-5.jpg

图7-5 插入对话框资源

6)修改对话框ID为“IDD_PAINT_DLG”后,再修改WinMain和消息回调函数代码。

978-7-111-46378-8-Chapter07-6.jpg

978-7-111-46378-8-Chapter07-7.jpg

7)编译并运行,测试代码,如图7-6所示。

本示例演示的是客户区标准绘图方式,通过绘图句柄在客户区内绘制了2个矩形和1条线。初学者需要掌握窗口客户区原点坐标位置,以及X轴和Y轴方向等客户区坐标系的特征。窗口客户区空间是不包括标题栏和边框的中央区域,坐标原点是位于客户区的左上角处,自左向右x坐标增大,自上向下y坐标增大,如图7-7所示。

978-7-111-46378-8-Chapter07-8.jpg

图7-6 查看运行结果

978-7-111-46378-8-Chapter07-9.jpg

图7-7 客户区坐标系

WM_PAINT是专用于窗口客户区显示更新消息,所有标准客户区绘画代码都必须要在此消息回调时执行。窗口非客户区绘画代码,就必须在WM_NCPAINT消息回调时执行。

8)修改对话框回调函数,添加非客户区绘图代码,并且在窗口移动时刷新界面。

978-7-111-46378-8-Chapter07-10.jpg(www.chuimin.cn)

978-7-111-46378-8-Chapter07-11.jpg

9)编译并运行,测试代码,如图7-8所示。

本示例绘制一个与窗口高和宽相同的矩形,并在标题栏区域画上一些红色文字。一个窗口的非客户区,不是只有标题栏和外边框那些细窄的空间,而是包括标题栏和外边框的整个窗口区域。窗口的客户区,就如同一块桌布遮盖在非客户区的中央位置。非客户区总面积大于等于客户区,只有边框部分被露出。窗口非客户区的坐标原点,是位于整个窗口的左上角处。

978-7-111-46378-8-Chapter07-12.jpg

图7-8 非客户区坐标系

10)继续修改对话框回调函数,添加临时客户区绘图代码。

978-7-111-46378-8-Chapter07-13.jpg

978-7-111-46378-8-Chapter07-14.jpg

11)编译并运行,测试代码,如图7-9所示。

每次在客户区单击鼠标左键,在单击的位置都会画出一个小圆。按<Alt+Tab>组合键切换窗口后,临时绘图产生的小圆都消失了,而标准绘图的矩形和直线都不消失。

12)对比三种绘图模式的关系。

978-7-111-46378-8-Chapter07-15.jpg

图7-9 查看运行结果

①标准客户区绘图。必须是在WM_PAINT消息回调时才能执行,调用BeginPaint函数获取标准客户区绘图句柄进行绘图,最后调用EndPaint函数释放。

②临时客户区绘图。在任何消息回调时都可以执行,调用GetDC获取临时客户区绘图句柄进行绘图,最后调用ReleaseDC释放。

③非客户区绘图。必须是在WM_NCPAINT消息回调时才能执行,调用GetWindowDC函数获取非客户区绘图句柄进行绘图,最后调用ReleaseDC释放。

④调用Invalidate函数强制客户区标准绘图更新,临时客户区的绘图被强制清除。

13)三种坐标的坐标体系,如图7-10所示。

在Windows程序开发中常用的三种坐标系是屏幕坐标系、客户区和非客户区窗口坐标系。

978-7-111-46378-8-Chapter07-16.jpg

图7-10 屏幕坐标系

①屏幕坐标系。坐标原点在屏幕的左上角,自左向右x坐标增大,自上向下y坐标增大。如果屏幕分辨率设置为1024×768,则屏幕左上角坐标是(0,0),屏幕右下角坐标是(1024,768)。GetWindowRect函数获取的窗口区域的坐标就是基于屏幕坐标系的,图7-10中窗口的左上角坐标为(110,80),右下角坐标(438,244),这些坐标都是依照屏幕坐标系计算的。

②(非客户区)窗口坐标系。坐标原点在窗口(包括标题栏)的左上角,x、y坐标轴方向与屏幕坐标系相同。GetWindowDC返回的设备环境是基于该坐标系的,该函数一般只在WM_NCPAINT消息中使用。

③客户区坐标系。坐标原点在标题栏以下的客户区左上角,x、y坐标轴方向与屏幕坐标系相同。BeginPaint函数返回的设备环境是基于客户区坐标系的,该函数一般只在WM_PAINT消息中使用。GetDC函数返回的设备环境也是基于客户区坐标系的,该函数可以在任何消息中使用。GetClientRect函数获取的是客户区矩形,是依据该坐标系计算的,左上角坐标一定是(0,0)。

坐标系转换。最常用的坐标系是屏幕和客户区坐标,因此,系统提供了两个坐标系转换的函数。ScreenToClient函数将屏幕坐标转换为客户区坐标。ClientToScreen函数将客户区坐标转换为屏幕坐标。

14)继续修改对话框回调函数,编写代码测试坐标系转换函数。

978-7-111-46378-8-Chapter07-17.jpg

978-7-111-46378-8-Chapter07-18.jpg

15)编译并运行,测试代码。

每次刚要单击“OK”按钮的时候它却“逃跑”了。为什么一定要转换为客户区坐标再移动呢?因为查看MSDN中关于MoveWindow的详细说明可以了解,移动主窗口时依据屏幕坐标系,移动子窗口时依据的是父窗口的客户区坐标系,如图7-11所示。

978-7-111-46378-8-Chapter07-19.jpg

图7-11 MoveWindow函数说明