Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2690811
  • 博文数量: 416
  • 博客积分: 10220
  • 博客等级: 上将
  • 技术积分: 4193
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-15 09:47
文章分类

全部博文(416)

文章存档

2022年(1)

2021年(1)

2020年(1)

2019年(5)

2018年(7)

2017年(6)

2016年(7)

2015年(11)

2014年(1)

2012年(5)

2011年(7)

2010年(35)

2009年(64)

2008年(48)

2007年(177)

2006年(40)

我的朋友

分类: WINDOWS

2007-10-21 16:35:41

作者:ftkghost   
[转载请注明出处:ftkghost.spaces.msn.com  blog.csdn.net/ftkghost]


1.首先介绍一下什么是DC(设备描述表)

Windows应用程序通过为指定设备(屏幕,打印机等)创建一个设备描述表(Device Context, DC)在DC表示的逻辑意义的画布上进行图形的绘制。DC是一种包含设备信息的数据结构,它包含了物理设备所需的各种状态信息。Win32程序在绘制图形之前需要获取DC的句柄HDC,并在不继续使用时释放掉。

2CDC及其派生类

CDC及其派生类的继承视图:
CObject
public |------CDC
public |------CClientDC
public |------CPaintDC
public |------CWindowDC
public |------CMetaFileDC
(注意: 除CMetaFileDC以外的三个派生类用于图形绘制.)
[以下几段是翻译MSDN中资料]
CDC类定义了一个设备描述表相关的类,其对象提供成员函数操作设备描述表进行工作,如显示器,打印机,以及显示器描述表相关的窗口客户区域。

通过CDC的成员函数可进行一切绘图操作。CDC提供成员函数进行设备描述表的基本操作,使用绘图工具, 选择类型安全的图形设备结构(GDI),以及色彩,调色板。除此之外还提供成员函数获取和设置绘图属性,映射,控制视口,窗体范围,转换坐标,区域操作,裁减,划线以及绘制简单图形(椭圆,多边形等)。成员函数也提供绘制文本,设置字体,打印机换码,滚动, 处理元文件。

 

通过CDC的成员函数可进行一切绘图操作。CDC提供成员函数进行设备描述表的基本操作,使用绘图工具, 选择类型安全的图形设备结构(GDI),以及色彩,调色板。除此之外还提供成员函数获取和设置绘图属性,映射,控制视口,窗体范围,转换坐标,区域操作,裁减,划线以及绘制简单图形(椭圆,多边形等)。成员函数也提供绘制文本,设置字体,打印机换码,滚动, 处理元文件。

其派生类:
PaintDC: 封装BeginPaint和EndPaint两个API的调用。
CClientDC: 处理显示器描述表的相关的窗体客户区域。
CWindowDC: 处理显示器描述表相关的整个窗体区域,包括了框架和控 件(子窗体)。
CMetaFileDC: 与元文件相关的设备描述表关联。

CDC提供两个函数,GetLayout和SetLayout用于反转设备描述表的布局。用于方便阿拉伯,希伯来的书写文化习惯的设计,以及非欧洲表中的字体布局。

CDC包含两个设备描述表,m_hDC和m_hAttribDC对应于相同的设备,CDC为m_hDC指定所有的输出GDI调用,大多数的GDI属性调用由m_hAttribDC控制。(如,GetTextColor是属性调用,而SetTextColor是一种输出调用。)

 

例子:框架使用这两个设备描述表,一个对象从物理设备中读取属性输出到元文件。打印机预览在框架中被执行时也是相同的形式。你也可以编写代码使用这两个设备描述表在你的应用程序中进行类似的操作。

 

3.使用方法

创建一个UseDC的MFC单文档程序,定制7个按钮用来选择使用的DC,每个按钮由一个成员变量标识控制[互斥],分别是

bool m_bHDCDisplay ---- 使用整个屏幕的HDC;
bool m_bHDC ---- 使用当前视图的对应的DC;
bool m_bCDC ---- 使用CDC类[当前视图窗体];
bool m_bCClientDC ---- 使用CClientDC类[视图客户区域DC];
bool m_bCPaintDC ---- 使用CPaintDC[视图窗体];
bool m_bCWindowDC ---- 使用CWindowDC[整个视图窗体];
bool m_bCMetaFileDC ---- 使用CMetaFileDC

添加7个按钮的响应函数以控制这些bool变量.(这里比较简单我就不提供代码了)

视图类构造函数:

CUseDCView::CUseDCView()

{

this->m_bHDCDisplay = false;
this->m_bHDC = false;
this->m_bCDC = false;
this->m_bCClientDC = false;
this->m_bCPaintDC = false;
this->m_bCWindowDC = false;
this->m_bCMetaFileDC = false;
m_hMetaFile = NULL;

}

视图类OnDraw函数

void CUseDCView::OnDraw(CDC* pDC)

{

CUseDCDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

//窗体在OnDraw中会自动传入关联当前视图窗体客户矩形区域的CPaintDC
//才能获取相应的设备描述表

HDC HDCDisplay = NULL; //屏幕DC的句柄 对应m_bHDCDisplay
HDC hDC = NULL; //普通DC的句柄 对应m_bHDC;
CDC * pCDC = NULL; //对应m_bCDC
CClientDC * pClientDC = NULL; //对应m_bCClientDC
CPaintDC * pPaintDC = NULL; //对应m_bCPaintDC
CWindowDC * pWindowDC = NULL; //对应m_bCWindowDC
CMetaFileDC * pMetaFileDC = NULL; //对应m_bMetaFileDC

HMETAFILE hMetaFile = NULL;

RECT rect; //定义一个设备左上角的矩形区域
rect.left = 0;
rect.top = 0;
rect.right = 200;
rect.bottom = 30;

//
注意: HDCDisplayhDC::Release()中第一个参数HWND
//
GetDC()的第一个参数必须对应。
if(m_bHDCDisplay)
{
HDCDisplay = ::GetDC(NULL); //
获得整个显示器区域的DC
::DrawText(HDCDisplay, "HDC of Display", 14, &rect, DT_LEFT|DT_TOP);
::ReleaseDC(NULL, HDCDisplay);
HDCDisplay = NULL;
}
if(m_bHDC) //
绘制在客户区域
{
hDC = ::GetDC(this->m_hWnd); //
本窗体的DC
::DrawText(hDC, "HDC", 3, &rect, DT_LEFT|DT_TOP);
::ReleaseDC(this->m_hWnd, hDC);
hDC = NULL;
}
else if(m_bCDC){
//
必须释放由程序框架传入的pDC才能获取当前客户区域设备描述表
pCDC = this->GetDC(); //
当前窗体(视图)的设备描述表
pCDC->DrawText("Use class CDC", 13, &rect, DT_LEFT|DT_TOP);
this->ReleaseDC(pCDC);
pCDC = NULL;
}
else if(m_bCClientDC){
pClientDC = new CClientDC(this); //
获取本窗体客户区域的DC
pClientDC->DrawText("Use class CClientDC", &rect, DT_LEFT|DT_TOP);
delete pClientDC;
pClientDC = NULL;
}
else if(m_bCPaintDC) { //
测试当前传入的CDC是不是CPaintDC
pPaintDC = (CPaintDC*)pDC;
pPaintDC->DrawText("Use class CPaintDC", &rect, DT_LEFT|DT_TOP);
}
else if(m_bCWindowDC){
pWindowDC = new CWindowDC(this); //
获取本窗体框架和客户区域的DC
//
注意:绘制字符串的矩形区域空白部分覆盖了视图子窗体的边缘.
pWindowDC->DrawText("Use class CWindowDC", &rect, DT_LEFT|DT_TOP);
delete pWindowDC;
pWindowDC = NULL;
}
else if(m_bCMetaFileDC){
//
传入pDC->m_hDC使用pDC绘制,图形在视图窗体左上角
pMetaFileDC = new CMetaFileDC();
pMetaFileDC->m_hDC = pDC->m_hDC;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
delete pMetaFileDC;
pDC->PlayMetaFile(m_hMetaFile);
}

}

MFC程序中使用CPaintDC在视图窗口中绘制图象时要注意,应该在OnPaint()编写关于CPaintDC相关的代码,而不是在OnDraw().但是请注意,如果使用OnPaint()函数响应WM_PAINT事件,OnDraw函数将会被屏蔽;

可以使用以下代码测试:

CUseDCView.hCUseDCView的类定义中语句DECLARE_MESSAGE_MAP()之前加上afx_msg void OnPaint(),在CUseDCView.cppBEGIN_MESSAGE_MAP (CUseDCView, CView)END_MESSAGE_MAP()之间加上ON_WM_PAINT()

void CUseDCView::OnPaint()

{
CPaintDC dc(this);
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 200;
rect.bottom = 30;
dc.Draw3dRect(&rect, (COLORREF)0xff0000, (COLORREF)0x0000ff);
}

使用CMetaFileDC

有兴趣可以在以下语句中可以尝试以下几种情况:

else if(m_bCMetaFileDC){

...
...

}

情况1:(与上面的OnDraw函数中相同)
//
传入pDC->m_hDC使用pDC绘制,图形在视图窗体左上角
pMetaFileDC = new CMetaFileDC();
pMetaFileDC->m_hDC = pDC->m_hDC;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
delete pMetaFileDC;
pDC->PlayMetaFile(m_hMetaFile);

情况2

//传入pDC->m_hDC使用屏幕DC绘制,图形在视图窗体左上角
HDC hdc;
pMetaFileDC = new CMetaFileDC();
hdc = ::GetDC(NULL);
pMetaFileDC->m_hDC = pDC->m_hDC;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
delete pMetaFileDC;
::PlayMetaFile(hdc, m_hMetaFile);
::ReleaseDC(NULL, hdc);

情况3

//传入屏幕DC,使用屏幕DC绘制,图像在屏幕左上角
HDC hdc;
pMetaFileDC = new CMetaFileDC();
hdc = ::GetDC(NULL);
pMetaFileDC->m_hDC = hdc;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
delete pMetaFileDC;
::PlayMetaFile(hdc, m_hMetaFile);
::ReleaseDC(NULL, hdc);

情况4

//传入屏幕DC,使用pDC绘制, 图像在屏幕左上角
HDC hdc;
pMetaFileDC = new CMetaFileDC();
hdc = ::GetDC(NULL);
pMetaFileDC->m_hDC = hdc;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
delete pMetaFileDC;
pDC->PlayMetaFile(m_hMetaFile);
::ReleaseDC(NULL, hdc);

情况5

//传入屏幕DC,使用pDC绘制,但是绘制前释放屏幕DC,
//
没有出错,而且图像绘制在屏幕左上角
HDC hdc;
pMetaFileDC = new CMetaFileDC();
hdc = ::GetDC(NULL);
pMetaFileDC->m_hDC = hdc;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
delete pMetaFileDC;
::ReleaseDC(NULL, hdc);
pDC->PlayMetaFile(m_hMetaFile);

情况6

//传入屏幕pDC->hDC,使用CMetaFileDC绘制,图像在视图窗体左上角
HDC hdc;
pMetaFileDC = new CMetaFileDC();
hdc = pDC->m_hDC;
pMetaFileDC->m_hDC = hdc;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
pMetaFileDC->PlayMetaFile(m_hMetaFile);
delete pMetaFileDC;
::ReleaseDC(NULL, hdc);

情况7

//传入屏幕DC,使用CMetaFileDC绘制,图像在屏幕左上角
HDC hdc;
pMetaFileDC = new CMetaFileDC();
hdc = ::GetDC(NULL);
pMetaFileDC->m_hDC = hdc;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
pMetaFileDC->PlayMetaFile(m_hMetaFile);
delete pMetaFileDC;
::ReleaseDC(NULL, hdc);

情况8

//传入屏幕DC,使用CMetaFileDC绘制,绘图前释放hdc
//
仍然没有出错,图像在屏幕左上角
HDC hdc;
pMetaFileDC = new CMetaFileDC();
hdc = ::GetDC(NULL);
pMetaFileDC->m_hDC = hdc;
pMetaFileDC->DrawText("Use class CMetaFileDC", &rect, DT_LEFT|DT_TOP);
pMetaFileDC->Draw3dRect(200, 0, 300, 30, (COLORREF)0xffff00, (COLORREF)0x0000ff);
m_hMetaFile = pMetaFileDC->Close();
::ReleaseDC(NULL, hdc);
pMetaFileDC->PlayMetaFile(m_hMetaFile);
delete pMetaFileDC;

由上面代码可见绘图的位置与传入CMetaFileDC::m_hDC的值有关。

使用CMetaFileDC需要注意:

使用CMetaFileDC对象调用一系列你想重复的CDCGDI命令,只能使用CDC类中GDI输出命令。CDCPlayMetaFile可以使用图元文件句柄来执行图元文件中的命令,图元文件也能使用CopyMetaFile使其存储在磁盘上。当不再需要图元文件时,调用DeleteMetaFile从内存中删除它。

阅读(2981) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-08-27 13:10:10

指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。Windows并不希望一般程序修改其内部数据结构,因为这样太不安全。所以Windows给每个使用GlobalAlloc等函数声明的内存区域指定一个句柄(本质上仍是一个指针,但不要直接操作它),平时你只是在调用API函数时利用这个句柄来说明要操作哪段内存。当你需要对某个内存进行直接操作时,可以使用GlobalLock锁住这段内存并获得指针来直接进行操作。

chinaunix网友2008-08-27 11:40:14

句柄 通常来说,句柄是对内部对象的索引。 它有可能是一个编号, 也有可能是一个对象指针, 对于不同系统,实现方式是不一样的。 对于使用者来说,不管句柄是否是指象对象的指针,都不要去当指针来引用。因为,首先,该对象的结构通常是不公开的;其次,即使你知道对象的结构,它也有可能在将来的版本中发生改变。而这些也是设计者使用句柄的原因。

chinaunix网友2008-08-27 10:54:40

MFC里,到底什么是句柄,就是指针吗? 所谓句柄实际上是一个数据,是一个Long (整长型)的数据。 句柄是WONDOWS用来标识被应用程序所建立或使用的对象的唯一整数,WINDOWS使用各种各样的句柄标识诸如应用程序实例,窗口,控制,位图,GDI对象等等。WINDOWS句柄有点象C语言中的文件句柄。 从上面的定义中的我们可以看到,句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样,但是,也可能有一个名字和你一样的人。从数据类型上来看它只是一个16位的无符号整数。应用程序几乎总是通过调用一个WINDOWS函数来获得一个句柄,之后其他的WINDOWS函数就可以使用该句柄,以引用相应的对象。 如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操