CDC的四個派生類CClientDC,CPaintDC,CWindowDC,CMetaFileD

xringm發表於2016-03-29

原文地址:http://2004ustb2008buaa.blog.sohu.com/111737011.html

CDC是Windows繪圖裝置的基類
CClientDC:
(1)(客戶區裝置上下文)用於客戶區的輸出,與特定視窗關聯,可以讓開發者訪問目標視窗中客戶區,其建構函式中包含了GetDC,解構函式中包含了ReleaseDC


CPaintDC:
(1)用於響應視窗重繪訊息(WM_PAINT)是的繪圖輸出。
(2)CPaintDC在建構函式中呼叫BeginPaint()取得裝置上下文,在解構函式中呼叫EndPaint()釋放裝置上下文。EndPaint()除了釋放裝置上下文外,還負責從訊息佇列中清除WM_PAINT訊息。因此,在處理視窗重畫時,必須使用CPaintDC,否則WM_PAINT訊息無法從訊息佇列中清除,將引起不斷的視窗重畫。
(3)CPaintDC也只能用在WM_PAINT訊息處理之中。


CWindowDC:
(1)可在非客戶區繪製圖形,而CClientDC,CPaintDC只能在客戶區繪製圖形。
(2)座標原點是在螢幕的左上角,CClientDC,CPaintDC下座標原點是在客戶區的左上角。
(3)關聯一特定視窗,允許開發者在目標視窗的任何一部分進行繪圖,包含邊界與標題,這種DC同WM_NCPAINT訊息一起傳送

說明:在繪圖時推薦使用CClientDC,CPaintDC和CWindowDC物件,而不推薦直接使用CDC物件。

例項:
CClientDC *pDC = new CClientDC(this);
CWindowDC dc(this);

 

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從記憶體中刪除它。

相關文章