DC概念:
当使用GDI函数比如MoveToEx、LineTo、TextOut时,只是告诉系统要画线或者写字了,但是用什么样的笔(HPEN),字是什么颜色(setTextColor),画在哪张‘纸’(HBITMAP)上需要从一个由系统定义的数据结构中去读取,这个数据结构被称为DeviceContext(DC)。话句话说,GDI函数知识绘画的动作,而DC则保存了绘画所需要的材料和工具。
问:DC、CDC、HDC、CClientDC、有什么本质区别?
答:都是DC嘛,HDC就是最原始的DC句柄,很多API的第一个参数就是一个HDC类型,比如
1 HDC hDC = ::GetDC( m_hWnd); 2 ::MoveToEx( hDC, 0, 0, NULL ); 3 ::LineTo( hDC, 0, 100 ); 4 ::ReleaseDC( m_hWnd, hDC );
在MFC中,为了将API封装成一个类来操作,因此多出来了一个CDC。所以在MFC中,都是
1 CDC dc = GetDC(); 2 dc.MoveTo( 0, 0 ); 3 dc.LineTo( 0, 100 ); 4 this->ReleaseDC( &dc );
但这样还不够,因为 CDC还要你自己去释放,所有MFC中又多出来一个CClientDC, 这样你就可以这样了:
1 CClientDC dc(this); 2 dc.MoveTo( 0, 0 ); 3 dc.LineTo( 0, 100 );
CClientDC的析构函数自己会释放自己。
DC不是什么对象,就是设备上下文的简称。与CClientDC一样,还有CWindowDC,CPaintDC,只是它们的绘制范围不一样。但弄到底,都只是HDC的一些封装而已,你可以在CDC类中直接引用 m_hDC,这就是那个原始的HDC句柄了。
CDC是MFC的DC的一个类
HDC是DC的句柄,API中的一个类似指针的数据类型。MFC类的前缀都是C开头的 H开头的大多数是句柄这是为了助记,是编程读/写代码的好的习惯。
CDC中所有MFC的DC的基类.常用的CClientDC dc(this);就是CDC的子类(或称派生类). CDC等设备上下分类,都含有一个类的成员变量:m_nHdc;即HDC类型的句柄. 记住下面的一句话,会有助于你的理解.
MFC的类,是在用window API语句开发出来的有一定功能的小程序,(也可称为类),使用它的默认方法,就是,记住它的名字与参数(可以用笔记,代替脑记). 如果将window api比做汇编语言,那么MFC就相当于Basic语言
下面介绍Vc中最重要的部分--MFC
CWnd:窗口,它是大多数“看得见的东西”的父类(Windows里几乎所有看得见的东西都是一个窗口,大窗口里有许多小窗口),比如视图CView、框架窗口CFrameWnd、工具条CToolBar、对话框CDialog、按钮CButton,etc;一个例外是菜单(CMenu)不是从窗口派生的。该类很大,一开始也不必学,知道就行了。
CDocument文档,负责内存数据与磁盘的交互。最重要的是OnOpenDocument(读入),OnSaveDocument(写盘),Serialize(读写)
CView视图,负责内存数据与用户的交互。包括数据的显示、用户操作的响应(如菜单的选取、鼠标的响应)。最重要的是OnDraw(重画窗口),通常用CWnd::Invalidate()来启动它。另外,它通过消息映射表处理菜单、工具条、快捷键和其他用户消息。你自己的许多功能都要加在里面,你打交道最多的就是它。
CDC设备文本。无论是显示器还是打印机,都是画图给用户看。这图就抽象为CDC。CDC与其他GDI(图形设备接口)一起,完成文字和图形、图像的显示工作。把CDC想象成一张纸,每个窗口都有一个CDC相联系,负责画窗口。CDC有个常用子类CClientDC(窗口客户区),画图通常通过CClientDC完成。
CDialog对话框
CWinApp应用程序类。似于C中的main函数,是程序执行的入口和管理者,负责程序建立、消灭,主窗口和文档模板的建立。最常用函数InitInstance():初始化。
CGdiObject及子类,用于向设备文本画图。它们都需要在使用前选进DC。
CPen笔,画线
CBrush刷子,填充
CFont字体,控制文字输出的字体
CBitmap位图
CPalette调色板
CRgn区域,指定一块区域可以用于做特殊处理。
CFile文件。最重要的不外是Open(打开),Read(读入),Write(写)
CString字符串。封装了C中的字符数组,非常实用。
CPoint点,就是(x, y)对
CRect矩形,就是(left, top, right, bottom)
CSize大小,就是(cx, cy)对(宽、高)
Windows使用与设备无关的图形设备环境(DC: Device Context) 进行显示。MFC基础类库定义了设备环境对象类----CDC类。
CDC与CGdiObject的关系
说到CDC类就不能不提一下GdiObject---图形对象类。在Windows应用程序中,设备环境与图形对象共同工作,协同完成绘图显示工作。就像画家绘画一样,设备环境好比是画家的画布,图形对象好比是画家的画笔。用画笔在画布上绘画,不同的画笔将画出不同的画来。选择合适的图形对象和绘图对象,才能按照要求完成绘图任务。
有关CDC类的继承
父类:从 CObject 直接继承而来。继承了CObject类的各种特性,如动态创建等等。
子类:CClientDC-------代表操作窗口的DC ,是比较常用的一个子类
CMetaFileDC ------响应Meta File的DC ,Meta File是一些GDI消息。
CPaintDC-------响应WM_PAINT消息的DC。
CWindowDC ------代表整个屏幕的DC
CDC类的数据成员 数据成员只有两个:
HDC m_hDC : CDC对象使用的输出设备上下文
HDC m_hAttribDC : CDC对象使用的属性设备上下文
二者在CDC对象创建时指向相同的设备上下文。
问:VC 解释一下CDC *pDC=pWnd->GetDC();
答:绘图用到的所有有关的类与函数都被集合到一起,被称之为设备上下文,或设备环境。
你可以将这个类集看成一个超级的大类。GetDC()是一个函数,它能获得DC的使用权,也就是说它将句柄(或指针)交给了你,也就是说它将使用它的钥匙交给了你。你可以使用它的所有函数了。不用再向API函数那样,每一个绘图动作都要使用一个函数,如果你不想改变它的默认值,可以直接绘图,当然,他提供了比API更加强大的函数与更加多的功能。当然,对初学者,最方便的是,不用记大量的函数了,当你用它实例化一个对象后(也称得到设备上下文(DC)),你只要用->或.就可以在VC提示的帮助下来选择相应的函数了。
CDC *pDC=pWnd->GetDC();
1.用CDC(MFC的设备上下文)实例化一个对象的指针
2.为这个对象的指针赋值为pWnd
3.pWnd被赋值为GetDC.相当于用API的DC实例化一个对象的指针pWnd
小结:用MFC的设备上下文实列化一个指针的对象,这个指针对象的值来源于API的设备上下文实例化。
==============
实际上,MFC的大多数调用的函数,最终调用的都是API里的相应的函数
问:VC++中绘图 用到的this->GetDC()函数是什么作用?还有 this->ReleaseDC(pDC)
答:
1、this指针是当前类的对象的指针.它指向类实例化后的对象.它是隐含的指针.每个对
象都有一个,使用它方便呀,不用自已再定义指针了.
--------------------------------------
2、this->GetDC();得到DC, 相当于
CDC *pDC;
pDC->GetDC();
也相当于
CDC dc;
dc.GetDC();
-----------------------------------
3、this->ReleaseDC()是释放DC
------------------------------------
4、DC的常被称为设备上下文,或设备环境.它是一个超大的类或函数的集合.它集合了,所有的绘图,打印,等相关的类及函数.你只要声明了它的对象或指针,你就得到了它的所有类及函数的使用权.象画笔,画刷,位图,绘图函数....等等全部的功能.你只要用/dc./this->/pDC->等方式就可以直接调用它的函数了.
CClientDC
类CClientDC派生于CDC,在构造时调用了Windows函数GetDC,在析构时调用了ReleaseDC。这意味着和CClientDC对象相关的设备上下文是窗口的客户区。
几种DC及区别
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消息处理之中。Detach();}CWindowDC:关联一特定窗口,允许开发者在目标窗口的任何一部分进行绘图,包含边界与标题,这种DC同WM_NCPAINT消息一起发送CWindowDC与CClientDC,CPaintDC的区别:CWindowDC可在非客户区绘制图形,而CClientDC,CPaintDC只能在客户区绘制图形。CWindowDC下坐标原点是在屏幕的左上角,CClientDC,CPaintDC下坐标原点是在客户区的左上角。
CClientDC与CPaintDC的区别:
CPaintDC的对象一般用在OnPaint内以响应Windows消息WM_PAINT,自动完成绘制,在整个窗口内进行重画,维持原有窗口完整性。CClientDC应用在非响应Windows消息WM_PAINT的情况下,进行实时绘制,绘制的区域内被重画。
CDC与CGdiObject的关系
说到CDC类就不能不提一下GdiObject---图形对象类。 在Windows应用程序中,设备环境与图形对象共同工作,协同完成绘图显示工作。就像画家绘画一样,设备环境好比是画家的画布,图形对象好比是画家的画笔。用画笔在画布上绘画,不同的画笔将画出不同的画来。选择合适的图形对象和绘图对象,才能按照要求完成绘图任务。
有关CDC类的继承
父类:从 CObject 直接继承而来。继承了CObject类的各种特性,如动态创建等等。
子类:CClientDC-------代表操作窗口的DC ,是比较常用的一个子类
CMetaFileDC ------响应Meta File的DC ,Meta File是一些GDI消息。
CPaintDC-------响应WM_PAINT消息的DC。
CWindowDC ------代表整个屏幕的DC
CDC类的数据成员
数据成员只有两个:
HDC m_hDC : CDC对象使用的输出设备上下文。
HDC m_hAttribDC : CDC对象使用的属性设备上下文。
二者在CDC对象创建时指向相同的设备上下文。
所需头文件:#include <afxwin.h>
设备上下文:
设备上下文是一种包含有关某个设备(如显示器或打印机)的绘制属性信息的 Windows 数据结构。所有绘制调用都通过设备上下文对象进行,这些对象封装了用于绘制线条、形状和文本的 Windows API。设备上下文允许在 Windows 中进行与设备无关的绘制。设备上下文可用于绘制到屏幕、打印机或者图元文件。
CPaintDC 对象将 Windows 的常见固定用语进行封装,调用 BeginPaint 函数,然后在设备上下文中绘制,最后调用 EndPaint 函数。CPaintDC 构造函数为您调用BeginPaint,析构函数则调用 EndPaint。该简化过程将创建 CDC 对象、绘制和销毁 CDC 对象。在框架中,甚至连这个过程的大部分也是自动的。具体说来,框架给 OnDraw 函数传递(通过 OnPrepareDC)准备好的 CPaintDC,您只需绘制到 CPaintDC 中。根据调用 OnDraw 函数的返回,CPaintDC 被框架销毁并且将基础设备上下文释放给 Windows。
CClientDC 对象封装对一个只表示窗口工作区的设备上下文的处理。CClientDC 构造函数调用 GetDC 函数,析构函数调用 ReleaseDC 函数。CWindowDC 对象封装表示整个窗口(包括其框架)的设备上下文。
CMetaFileDC 对象将绘制封装到 Windows 图元文件中。与传递给 OnDraw 的 CPaintDC 相反,在这种情况下您必须自己调用 OnPrepareDC。
设备上下文(Device Context)DC
DC实际上是GDI内部保存的数据结构。
DC与特定的显示设备(如显示器或打印机)相关。
对于显示器,DC总是与显示器上的特定视窗相关。
DC中的有些值是图形「属性」,这些属性定义了GDI绘图函数工作的细节。
例如,对於TextOut,DC的属性确定了文字的颜色、文字的背景色、x座标和y座标映射到视窗的显示区域的方式,以及显示文字时Windows使用的字体。
MSDN的解释: 一个DC是一个结构,它定义了一系列图形对象的集合以及它们相关的属性,以及影响输出效果的一些图形模式。这些图形对象包括一个画线的笔,一个填充和painting的画刷,一个用来向屏幕拷贝的位图,一个定义了一系列颜色集合的调色板,一个用来剪裁等操作的区域,一个做painting和drawing操作的路径。
一个应用程序从不直接地访问(access)dc,常见的取得dc的方式有以下几种:
SDK's way:
1. BeginPaint
case WM_PAINT: HDC hdc = BeginPaint(hwnd, &ps); EndPaint(hwnd, &ps);
MSDN的解释: BeginPaint函数自动地设置dc的剪裁区域,这个剪裁区域,剪裁的是由InvalidateRect 或 InvalidateRgn 函数触发的窗口无效区域,或者是系统给出的无效区域,当窗口被sizing, moving, creating, scrolling, or any other operation that affects the client area.
一个应用程序从不调用BeginPaint,除了在收到一个WM_PAINT消息的时候;每一BeginPaint调用之后,需要调用EndPaint函数。
2.GetXXXDC
GetDC取得与窗口客户区相关的dc,GetWindowDC取得与整个窗口(包括客户区和非客户区)相关的dc。
还有一类重要的dc,内存DC,是一个虚拟的内存设备上下文,我们对它进行绘图等操作,不会显示在屏幕或打印机上,而我们可以在它完成之后,拷贝到屏幕上或打印机上来输出,这样我们可以避免因为操作而给屏幕带来的闪烁,对于打印机而言,打印只能是从上往下打,而我们在MEMDC中,可以随意进行操作,这样可以输出直接在打印
机上输出所达不到的效果.
在窗口上贴图一般总是要用到内存DC,将所有的绘制工作先绘制在内存DC上,然活一次性拷贝到屏幕DC上,就是这样了。
这里是使用mfc进行的说明,对hdc进行了封装,但是道理是一样的。
1.创建内存DC
CDC m_MenDC; //声明内存DC
CDC m_MenDC2; //声明内存DC
CBitmap m_Bitmap1; //声明一个位图
m_MenDC.CreateCompatibleDC(GetDC());//创建内存DC
m_MenDCMap.CreateCompatibleDC(GetDC());//创建内存DC
m_Bitmap1.CreateCompatibleBitmap(GetDC(),1024,768); //创建一个兼容位图,这是一个空的位图,我们可以把它想象成一个屏幕,可以在上面画线,输出文字等,自己制作一个简单的位图。
m_hbmpBK = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),path+"Bk4.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE); //我们也可以从硬盘导入一张位图。
2,为内存DC选入一张位图,或兼容位图。
m_MenDC.SelectObject(m_hbmpBK);
m_MenDC2.SelectObject(m_Bitmap1);
注意,想要在内存dc上作图,必须先为它选入一张位图,或兼容位图。想想前面,不管BeginPaint还是getDC都有一个相关的区域。这里的位图就是相关区域。
3.接着我们就可以像在窗口上作图一样,使用gdi函数(drawline,textout...)在内存dc上作图了。同时也可以从一个内存dc拷贝位图到另一个内存dc。
m_MenDC2.BitBlt(0,0,1024,768,&m_MenDC,0,0,SRCCOPY);
4,绘制结果的显示,将这些东西拷到屏幕DC(getdc取得的dc)上
// 所谓的双缓冲就是把所有的绘制工作都做在一个内存DC上。
// 最后一次拷到屏幕DC上,只能有一次
dc.BitBlt(0,0,1024,768,&m_MenDC2,0,0,SRCCOPY);//这里的dc是通过getdc取得的屏幕或者某个窗口的dc。
这里所强调的“一次”;是不要连续将几个内存DC的内容都拷到屏幕DC上,这样没有起到双缓冲的效果。如果你搞了很多个内存DC,想把这些东西都显示出来,那你应该先把这多个内存DC的内容同时拷到另外一个内存DC上,再把这个内存DC的内容拷到屏幕DC上