设备上下文和图形设备接口
Windows操作系统为用户提供了与具体设备无关的图形设备接口(GDI, Graphic Device Interface),用它来管理图形操作,它利用驱动程序使显示器、打印机等输出设备与应用程序分离。在编程过程中,我们不必关心硬件的具体实现,可以直接进行抽象操作。
设备上下文(DC, DeviceContext)是显示器、打印机等物理设备的逻辑模型,被称为设备描述表、设备环境或设备文本。设备上下文定义了绘图所需要的一套图形对象以及他们的属性,这些属性包括画笔、画刷、位图、调色板等。而真正的绘图函数在GDI中,包括画线、画椭圆、填充图形等。形象的讲,DC就是普通绘图所以需的一些物质条件,包括使用什么样的笔、什么样的纸等;而GDI则是画家的思想,利用DC中提供的物质条件进行作画:画线、画椭圆、填充图形等。
设备上下文类 CDC
在MFC中,设备上下文和图形设备接口被封装在一起,建立了CDC类。CDC是CObject的知己诶派生类,封装了windows API中的绝大部分GDI函数。
1、 数据成员:CDC类中包含两种类型的设备上下文,m_hDC和m_hAttribDC,创建CDC对象时它们指向相同的设备。CDC在执行所有与输出GDI有关的调用时使用m_hDC,执行大部分与属性GDI有关的调用时使用m_hAttribDC.
2、 构造与初始化函数
Attach()将windows的设备上下文与CDC对象联系在一起;
CreateCompatibleDC() 创建一个内存设备上下文与另一个设备上下文兼容,用户可以使用它在内存中准备图像;
CreateDC() 为一个特定的设备创建设备上下文;
Detach() 将CDC对象鱼windows设备上下文分离;
GetCurrentBitmap() 返回一个指向CBitmap对象的指针;
GetCurrentBrush() 返回一个指向选定CBrush对象的指针
SetAttribDC() 设置m_hAtttribDC
SetOutputDC() 设置m_hDC
3、 选择GDI绘图对象
CDC类提供了两个函数将对象选进设备上下文中:
(1) SelectObject函数,常用于装入一些自定义的GDI对象。有许多模型:
CPen* SelectObject(CPen* pPen);
CBrush* SelectObject(CBrush* pBrush);
CBitmap* SelectObject(CBitmap* pBitmap);
函数的参数为指向当前选定的画笔、画刷、图像等CGdiObject对象的指针,这些函数的返回值是指向被替换的CGdiObject对象的指针。
(2) SelectStockObject函数,为使用方便,CDC类把一些经常用到的CGdiObject对象进行预定义并存储了起来,可以使用SelectStockObject将这些预定义的库存对象选进设备上下文。
Virtual CGdiObject*SelectStockObject(int nIndex);
函数参数为nIndex, 取值和参数说明:
取值 | 说明 |
BLACK_BRUSH | 黑色画刷 |
DKGRAY_BRUSH | 深灰色画刷 |
GRAY_BRUSH | 灰色画刷 |
ANSI_FIXED_FONT | ANSI修正系统字体 |
SYSTEM_FONT | 系统字体 |
等等。。
4、 绘图函数
(1) 设置当前位置
在CDC类中,许多情况下画笔都是从当前位置开始绘图,使用成员函数GetCurrentPosition()来获得画笔当前位置,CPoint GetCurrentPosition() const;
使用MoveTo函数可以改变当前位置的坐标,则当前位置可以被设置到任何位置。CPoint MoveTo(int x, inty); CPoint MoveTo(POINT point);
(2) 画直线
BOOL LineTo(intx, int y);
BOOL LineTo(POINTpoint);
这个函数使用当前画笔从当前位置绘制一条线到参数中制定的位置(x, y) 或point,并把终点设置成当前位置。如果在两个任意点之间画线,则必须先使用MoveTo函数将当前位置移动到其中一点,然后使用LineTo函数。
(3) 画矩形
BOOL Rectangle(int x1, int y1, int x2, int y2);
BOOL Rectangle(LPRECT lpRect);
Rectangle函数绘制出的矩形角是方的,可以使用RoundRect来绘制圆角矩形;
BOOL RoundRect(int x1, int y1, int x2, int y2, int x3, int y3);
BOOL RoundRect(LPCRECTlpRect, POINT point);
(4) 画弧
BOOL Arc(int x1,int y1, int x2, int y2, int x3, int y3, int x4, int y4);
BOOL Arc(LPRECTlpRect, POINT start, POINT end);
其中参数x1, y1, x2, y2 或lpRect确定了弧线所在椭圆的外接矩形,(x3, y3)或start是弧线的起点,(x4, y4)或end是弧线的终点。
ArcTo函数也是CDC类提供的画弧函数,和Arc函数的不同之处在于ArcTo函数将当前位置移动至弧线的终点。
(5) 画椭圆/圆
BOOL Ellipse(int x1, int y1, int x2, int y2);
BOOL Ellipse(LPRECT lpRect);
使用外接矩形确定椭圆位置。
(6) 画多边形
BOOL Polyline(LPPOINT lpPoint, int nCount);
参数lpPoint是一个指向POINT结构或CPoint对象的数组的指针, nCount表示数组中元素的个数。该函数从数组中的第一个点画起,连接数组中其他点。它不使用也不更新当前点的位置。
Polygon()函数用于绘制多边形;BOOL Polygon(const POINT* lpPoint, const DWORD* lpPolyPoints, int nCount);
和绘制折线的函数类似,但在最后连接成闭合图形。
(7) 设置背景
默认情况下,绘制图形或输出文本时,背景颜色为白色。CDC提供了SetBackColor()函数来设置新的背景颜色。
Virtual COLORREF SetBackColor(COLORREF crColor);
若将背景色改为红色:SetBackColor(RGB(255,255,0));
还有画弦、画饼形。
5、 文本处理函数
(1) 文本绘制
CDC类提供了DrawText函数在指定的矩形内绘制格式文本,函数原型如下:
Virtualint DrawText(LPCTSTR lpszString, int nCount, LPRECT lpRect, UINT nFormat);
Int DrawText(const CString& str, LPRECT lpRect, UINT nFormat);
参数lpszString是一个指向被绘制的字符串的指针,如果nCount 为 -1,这个字符串必须是一个完整的字符串(以\0 结尾)。参数nCount指定了字符串中字符的个数。参数lpRect是一个指向包含文本所在矩形区域的RECT结构或CRect对象的指针。参数str是一个CString对象,包含即将被绘制的字符串,参数nFormat用于定制文本的格式。
CDC还提供了功能更为强大的文本绘制函数DrawTextEx。
Virtual int DrawTextEx(LPTSTR lpszString, int nCount, LPRECT lpRect, UINT nFormat, LPDRAWTEXTPARAMS lpDTParams);
(2) 文本输出
Virtual BOOLTextOut(int x, int y, LPCTSTR lpszString, int nCount);
BOOL TextOut(int x, int y, CString & str);
参数(x, y)指定文本输出的起始位置;参数lpszString是一个指向即将输出文本的字符串的指针;参数nCount指定字符串中字符的个数;参数str是一个CString 对象,包含了即将输出的字符串。
CDC还提供了更为强大的文本输出函数 TextOutEx()
(3) 设置文本属性
SetTextColor用于设置文本颜色。Virtual COLORREF SetTextColor(COLORREF crColor);
SetBkColor设置文本的背景颜色,SetBkMode设置背景模式;SetTextAlign设置文本对齐标志。
6、 CDC的派生类
(1) CClientDC类
一般情况下,窗口的客户区域并不包括边框、标题栏,因此如果创建了一个CClientDC对象,该设备上下文的映射区域也仅限于客户区域,即用户只能在客户区域内绘图。
CClientDC对象封装了一个只表示窗口客户区的设备上下文的处理。在CClientDC的构造函数中调用GetDC函数,在析构函数中调用ReleaseDC函数。
(2) CMetaFileDC类
CMetaFileDC对象将绘制封装到Windows图元文件中,它允许用户为以后的重新绘制提供了一系列的绘图命令。
(3) CPaintDC类
CPaintDC主要用于OnPaint函数中,如果重载OnPaint函数,就需要使用它。
CPaintDC类的构造函数自动调用BeginPaint函数,它的析构函数会自动调用EndPaint函数,因此,如果设备环境是在堆栈中创建,则会自动调用EndPaint函数。
(4) CWindowDC类
和CClientDC类相似,但CWindowDC对象封装整个窗口(包括其框架)的设备上下文,因此可以在应用程序的整个窗口上画图。CWindowDC类的构造函数调用GetWindowDC函数,析构函数调用ReleaseDC函数。
7、 CGdiObject类及其派生类
由于windows提供了在设备上下文中使用的多种绘图工具,如画笔、画刷、字体、位图等,为此MFC提供了等效于windows绘图工具的图形对象类CGdiObject,它有多个派生类,分别对应于不同的GDI 对象。
通常创建CGdiObject的派生类的对象,构造方法可以使用两步构造,如:
CPen mPen;
mPen.CreatePen(PS_DOT, 5, RGB(0, 0, 0));
在绘图操作需要GDI对象时,通常需要执行四个步骤的操作:
(1) 使用一步构造或两步构造方法初始化GDI对象
(2) 选择对象并将其放到当前的设备上下文中,使用SelectObject和SelectStockObject
(3) 处理完当前GDI对象后,选择旧GDI对象,将其放回设备上下文中以恢复其状态
(4) 允许框架分配的GDI对象在退出范围时被自动删除
需要注意的是,在退成程序前,必须将GDI对象从设备上下文中分离出来,将其删除。在Win32中,GDI对象占用进程的内存,虽然他在程序终止时会被自动释放,但一个没有被释放的GDI对象(例如位图对象)可能会占用很多内存。
8、 映射模式
WindowsGDI支持两套坐标系统:设备坐标系统和逻辑坐标系统。映射模式就是定义逻辑坐标的单位与设备坐标单位之间的关系。
设备坐标系统是物理设备上的坐标系统,设备坐标的基本单位是像素。设备坐标的原点始终在左上角,向右移动,横坐标增加;向下移动,纵坐标增加。不同的设备分辨率不同,在转换时需要注意。
在使用CDC类提供的绘图函数时,都是在逻辑坐标系中进行绘制,只有按照需要,适当的映射到设备坐标系中,才能够在显示器等输出设备上正确显示。
相对于设备坐标系统,逻辑坐标系统更加复杂同时更加灵活。在默认的映射模式下,逻辑坐标也以像素为单位,向右移动时横坐标增加,向下移动时纵坐标增加,并且初始时坐标原点在左上角。
Windows包含八种不同的映射模式,可以使用CDC类的SetMapMode函数设置当前的映射模式,函数模型为:
Virtual int SetMapMode(int nMapMode)
八种映射模式:
(1) MM_TEXT模式
默认的映射模式,设备坐标系统和默认的逻辑坐标系统都采用MM_TEXT模式,它的坐标被映射成像素。X轴正方向向右,Y轴正方向向下,初始时坐标原点在左上角。但可以使用CDC类的SetViewportOrg和SetWindowOrg函数来改变坐标原点的位置,函数原型:
CPoint SetWindowOrg(int x, int y)
CPoint SetWindowOrg(POINT point)
Virtual CPointSetViewportOrg(int x, int y)
Virtual CPoint SetViewportOrg(POINT point)
(2) 约束映射模式
Windows的映射模式中有六种都是约束映射模式,其中MM_TEXT也是约束映射模式的一种。他们的共同点是横纵轴单位的比例是固定的,应用程序无法改变映射到设备单位的逻辑单位数目。在这六种模式下,一个逻辑坐标单位对应的大小如下:
约束映射模式 | 逻辑坐标的大小 |
MM_HIENGLISH | 0.001英寸 |
MM_HIMETRIC | 0.01毫米 |
MM_LOENGLISH | 0.01英寸 |
MM_LOMETRIC | 1毫米 |
MM_TEXT | 1个设备单位 |
MM_TWIPS | 1/1440英寸,常用于打印机 |
上述六种约束模式中,除MM_TEXT外,其他五种映射模式的X轴正方向向右,Y轴正方向向下,其余都相反。
(3) 非约束映射模式
Windows提供两种映射模式:MM_ISOTROPIC和MM_ANISOTROPIC,允许改变横纵轴单位的比例。它使用两个矩形区域(窗口和视口)推导出横纵轴的比例和坐标方向。窗口使用的是逻辑坐标,视口使用设备坐标,原点可以是矩形区域的任意一个角。,
创建文档、窗口和视图
MFC 文档视图结构中各个类的关系:
文档保留该文档的视图列表以及指向创建该文档的文档模板的指针;视图保留指向其文档的指针,视图窗口是文档框架窗口的子窗口;文档框架窗口中保留指向当前活动视图的指针;文档模板保留期已打开文档的列表;windows跟踪所有打开的窗口,以便可以向这些窗口发送消息。所有这些关系都是在文档/视图结构的创建期间建立的,通过调用全局函数AfxGetApp,任何对象均可以获得指向应用程序对象的指针。
MFC各对象之间的访问
对象 | 访问其他对对象的方法 |
文档 | 使用GetFirstViewPosition和GetNextView访问文档的视图列表,调用GetDocTemplate获取文档模板 |
视图 | 调用GetDocument获取文档,调用GetParentFrame获取框架窗口 |
文档框架窗口 | 调用GetActiveView获得当前视图,调用GetActiveDocument获取当前视图的文档 |
MDI框架窗口 | 调用MDIGetActive获得当前活动的CMDIChildWnd |
CArchive类与序列化
序列化是指将对象写入永久性存储媒体(如磁盘文件)或从其中读取对象的过程。MFC将CArchive对象作为被序列化对象和存储媒体之间的中间物,它时钟与CFile对象相关联,它从CFile对象中获得序列化所需的信息,包括文件名以及执行的是读取还是写入操作。
在使用CArchive对象之前,必须先创建一个CFile对象。同时保证CArchive对象与文件打开方式一致。对于一个CArchive对象,既可以进行存储操作也可以进行读取操作,但二者无法同时进行。