【轉自】http://blog.chinaunix.net/u/28371/showart.php?id=404687
1.首先介紹一下什么是DC(設備描述表)
Windows應用程序通過為指定設備(屏幕,打印機等)創建一個設備描述表(Device Context, DC)在DC表示的邏輯意義的“畫布”上進行圖形的繪制。DC是一種包含設備信息的數據結構,它包含了物理設備所需的各種狀態信息。Win32程序在繪制圖形之前需要獲取DC的句柄HDC,并在不繼續使用時釋放掉。
2.CDC及其派生類
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;
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
RECT rect; //定義一個設備左上角的矩形區域
rect.left = 0;
rect.top = 0;
rect.right = 200;
rect.bottom = 30;
//注意: HDCDisplay和hDC的::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.h,CUseDCView的類定義中語句DECLARE_MESSAGE_MAP()之前加上afx_msg void OnPaint(),在CUseDCView.cpp中BEGIN_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對象調用一系列你想重復的CDC的GDI命令,只能使用CDC類中GDI輸出命令。CDC的PlayMetaFile可以使用圖元文件句柄來執行圖元文件中的命令,圖元文件也能使用CopyMetaFile使其存儲在磁盤上。當不再需要圖元文件時,調用DeleteMetaFile從內存中刪除它。