在单任务环境如 MS-DOS中,运行中的应用程序随时可自由地做它想做的事情,无论是在屏幕上画一条线,重新编写适配器的调色板,还是转换到另一种图像模式。而在窗口化多任务环境如Windows中,程序则无此自由。因为程序 A 的输出必须与程序 B 的输出相隔离。首先这意味着各程序的输出必须限制在自己的窗口中。 GDI使用一简单的机制保证在窗口中画图的各程序遵循这些规则。这种机制即为设备描述表(DC)。
当 Windows程序在屏幕、打印机或其他输出设备上画图时,它并不是将像素直接输出到设备上,而是将图绘制到由设备描述表(DC)表示的逻辑意义上的“显示平面”上去。设备描述表是探寓于 Windows 中的一种数据结构,它包含 GDI需要的所有关于显示平面情况的描述字段,包括相连的物理设备和各种各样的状态信息。在平面上画图之前, Windows 程序从 GDI获取设备描述表句柄,并在每次调用 GDI 输出函数时将句柄返回给 GDI 。若无有效的设备描述表句柄,则 GDI不会画第一个像素点。通过设备描述表, GDI 可确保程序所画的任何图形都能剪贴到屏幕的特定区域。设备描述表在使 GDI摆脱设备限制的过程中发挥了重要的作用。获得设备描述表句柄后,同一 GDI 函数可用来向多种输出设备上画图。
在使用 MFC 编制Windows程序时,设备描述表具有更加突出的作用。除了可作为通往各种输出设备的桥梁之外,设备描述表对象还封装了程序用来产生输出的 GDI函数。在 MFC 中,您不用捕获设备描述表句柄和调用 GDI输出函数,至少不必直接捕获和调用。而是通过创建一设备描述表对象并调用它的成员函数来画图。 MFC 的 CDC 类将 Windows设备描述表和获取设备描述表句柄的 GDI 函数就近封装在一起,而 CDC 派生类如 CPaintDC 和 CClientDC 则代表Windows应用程序使用的不同类型的设备描述表。
在 MFC应用程序中获取设备描述表的一种方法是调用CWnd::GetDC,它返回指向表示 Windows 设备描述表的 CDC对象的指针。在画图完毕时,要用 CWnd::ReleaseDC 释放由 CWnd::GetDC获取的设备描述表指针。
若同样的程序代码出现在 OnPaint 处理程序中时,则需用 CWnd::BeginPaint 和CWnd::EndPaint分别代替 GetDC 和 ReleaseDC ,以保证合理地处理 WM_PAINT 消息。
GDI还支持存储GDI命令序列的元文件,这些命令可重新执行以产生实际输出。为获取元文件输出的设备描述表,还要使用另一套函数来获取和释放 CDC指针。而且,为获取允许在窗口内任一地方圆图的设备描述表 CDC 指针(与只允许在窗口客户区画图的设备描述表 CDC 指针不同),需要调用CWnd::GetWindowDC而不是 GetDC ,但仍用 ReleaseDC来释放设备描述表。
详情可参考下表:
CPaintDC 用于在窗口客户区画图(仅限于 OnPaint处理程序)
CClientDC用于在窗口客户区画图(除 OnPaint外的任何处理程序)
CWindowDC 用于在窗口内任意地方画图,包括非客户区
CMetaFileDC 用于向 GDI元文件画图
这些类被设计为可直接进行实例化。各个类的构造函数和析构函数调用相应的函数捕获和释放相应的设备描述表,从而使得设备描述表的使用非常方便简捷:CPaintDcdc(this);传送给类构造函数的指针确定了设备描述表所属的窗口。
当在栈上构造设备描述表对象时,若对象的生命周期结束,则它的析构函数会被自动调用。而且析构函数一旦被调用,设备描述表就会被返回给 Windows 。在堆上用 new创建设备描述表时,要注意亲自释放设备描述表。
示例如下: CPaintDC* pDC = newCPaintDC(this);在这种情况下,有必要在创建设备描述表的函数结束之前执行删除语句 deletepDC;一边调用对象的析构函数和释放设备描述表。
在某些场合下,在堆上创建设备描述表要比在栈上创建更有用,但通常在栈上创建设备描述表对象并让编译程序执行删除任务会使您的编程轻松很多。CPaintDC MFC 的 CPaintDC 类响应 WM_PAINT消息,允许您在窗口客户区画图。但您只能在 OnPaint 处理程序中,而而不能在其他地方使用它。WM_PAINT消息在一个很重要的方面不同于其他 Windows 消息,如果处理程序调用 Windows 的 ::BeginPaint 和::EndPaint函数失败(或其他等价函数),那么不管有多少绘图工作,都不能将该消息从消息对列中删除。因此,应用程序将一遍又一遍地处理同一个 WM_PAINT消息而陷入死循环。而 CPaintDC 能保证这种情况不会发生,因为 CPaintDC 通过其构造函数和析构函数调用 ::BeginPaint和 ::EndPaint。CClientDC CWindowDC: CClientDC 可以在非 OnPaint (比如OnLButtonDown) 函数中创建一个 DC。例如:void CMainWindow::OnLButtonDown(UINTnFlags, CPoint point){ CRect rect; GetClientRect(&rect); CClientDCdc(this); dc.MoveTo(rect.left, rect.top); dc.LineTo(rect.right,rect.bottom);} 如果您不仅要使用窗口客户区,还要使用非客户区(标题栏、窗口边框等),则可以使用CWindowDC类。 还有一种更少见的场合是程序需要全屏幕的访问权。此时可创建 CClientDC 或 CWindowDC对象,并给其构造函数传送一个 NULL指针。设置设备描述表的属性: CDC::SetTextColor,CDC::SetBkColor,CDC::SetBkMode,CDC::SetMapMode,CDC::SetROP2,CDC::MoveTo(移动当前操作点),CDC::SelectObject(选择当前画笔,画刷或字体)。
每当从 Windows 中获取设备描述表时,设备描述表都被设置为默认值。因此,如果想在响应 WM_PAINT消息时使用红色画笔和蓝色笔刷画您的窗口,则每逢 OnPaint 被调用时都要将所需的画笔和笔刷选入设备描述表,也就是创建一个新的CPaintDC对象。否则,将使用默认的画笔和笔刷。如果不想在使用设备描述表时反复对它进行初始化设定,那么可用 CDC::SaveDC函数保存它的状态,并在下次使用时用 CDC:RestoreDC 将它恢复。
另一种方法是:注册一个自定义的 WNDCLASS ,其中包含CS_OWNDC样式,它使 Windows 为每个应用程序实例分配它已设置好的设备描述表。(有一个相关但很少使用的 WNDCLASS样式,CS_CLASSDC,它分配一个“半私有”设备描述表。该设备描述表可被同一 WNDCLASS创建的所有窗口共享。)将红色画笔和蓝色笔刷选人某个私有设备描述表后,如果没有被显示地替换,则它们依旧处于选中状态。
绘图模式: GDI将像素点输出到逻辑显示平面上时,它不只是简单地输出像素点颜色。相反,它通过一系列的布尔运算将输出像素点的颜色和输出目标位置上像素点的颜色合成在一起。(如果您在工程中需要使用到诸如“橡皮线”之类的功能时,可以详细参看次选项的有关帮助。)映射模式: 简单地说,映射模式是设备描述表的属性,用于确定从逻辑坐标值到设备坐标值的转换方式。(如果您在工程中需要使用到诸如以厘米为单位画图等功能时,可以详细参看次选项的有关帮助。) 调用CDC::LPtoDP函数可将逻辑坐标值转换为设备坐标值。反之,调用 CDC::DPtoLP函数可将设备坐标值转换为逻辑坐标值。 默认方式下,设备描述表的原点位于显示平面的左上角。即使改变映射模式.也不会改变原点的位置。然而,同改变映射模式一样,您也可以移动原点。 MFC 的 CDC 类提供了两个可移动原点的函数。 CDC::SetWindowOrg 移动窗口的原点,CDC::SetViewportOrg移动视口的原点。正常情况下,只能使用其中之一。同时使用两个会搞得一团糟。
用户坐标值是原点设立在窗口客户区左上角的设备坐标值。屏幕坐标值是原点位于屏幕左上角的设备坐标值。调用CWnd::ClientToScreen和 CWnd::ScreenToClient函数可实现用户坐标值与屏幕坐标值之间的转换。获取设备信息:CDC::GetDeviceCaps。
用 GDI 绘图: CDC中的一些图形函数:
MoveTo 在画线前设定当前位置
LineTo 从当前文职画一条线到指定位置(不包含指定位置点),并将当前位置移至线的终点。Polyline 将一系列点用线段连接起来
polylineTo 从当前位置开始将一系列点用线段连接起来,并将当前位置移至折线的终点。
Arc 绘一个弧
ArcTo 画一个弧并将当前位置移至弧的终点
PolyBezier 画一条或多条贝塞尔样条曲线
PolyBezierTo 一条或多条贝塞尔样条曲线,并将当前位置移至最后—段样条曲线的终点
PolyDraw 通过一组点画一系列线段和贝塞尔样条曲线.并将当前位置移至最后一个线段或样条曲线的终点
Chord 画一个由椭圆和直线相交后围成的封闭图形
Ellipse 画一个圆或椭圆
Pie 画一个饼状的楔形物
Polygon 连接一组点形成一个多边形
Rectangle 画一个带直角的矩形
RoundRect 画一个带圆角的矩形
注意:
CClientDC(客户区设备上下文)用于客户区的输出,它在构造函数中封装了GetDC(),在析构函数中封装了ReleaseDC()函数。一般在响应非窗口重画消息(如键盘输入时绘制文本、鼠标绘图)绘图时要用到它。用法是:CClientDC dc(this);//this一般指向本窗口或当前活动视图dc.TextOut(10,10,str,str.GetLength());//利用dc输出文本,如果是在CScrollView中使用,还要注意调 //用OnPrepareDC(&dc)调整设备上下文的坐标。CPaintDC用于响应窗口重绘消息(WM_PAINT)是的绘图输出。CPaintDC在构造函数中调用BeginPaint()取得设备上下文,在析构函数中调用EndPaint()释放设备上下文。EndPaint()除了释放设备上下文外,还负责从消息队列中清除WM_PAINT消息。因此,在处理窗口重画时,必须使用CPaintDC,否则WM_PAINT消息无法从消息队列中清除,将引起不断的窗口重画。CPaintDC也只能用在WM_PAINT消息处理之中。