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消息處理之中。
使用CPaintDC、CClientDC、CWindowDC的方法
首先,定義一個這些類的實例變量,通常在棧中定義。然後,使用它。
例如,MFC中CView對WM_PAINT消息的實現方法如下:
void CView::OnPaint()
{
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
在棧中定義了CPaintDC類型的變量dc,隨着構造函數的調用獲取了設備描述表;設備描述表使用完畢,超出其有效範圍就被自動地清除,隨着析構函數的調用,其獲取的設備描述表被釋放。
如果希望在堆中創建,例如
CPaintDC *pDC;
pDC = new CPaintDC(this)
則在使用完畢時,用delete刪除pDC:
delete pDC;
直接使用CDC
需要注意的是:在生成CDC對象的時候,並不像它的派生類那樣,在構造函數裏獲取相應的Windows設備描述表。最好不要使用::GetDC等函數來獲取一個設備描述表,而是創建一個設備描述表。其構造函數如下:
CDC::CDC()
{
m_hDC = NULL;
m_hAttribDC = NULL;
m_bPrinting = FALSE;
}
其析構函數如下:
CDC::~CDC()
{
if (m_hDC != NULL)
::DeleteDC(Detach());
}
在CDC析構函數中,如果設備描述表句柄不空,則調用DeleteDC刪除它。這是直接使用CDC時最好創建Windows設備描述表的理由。如果設備描述表不是創建的,則應該在析構函數被調用前分離出設備描述表句柄並用::RealeaseDC釋放它,釋放後m_hDC爲空,則在析構函數調用時不會執行::DeleteDC。當然,不用擔心CDC的派生類的析構函數調用CDC的析構函數,因爲CDC::~CDC()不是虛擬析構函數。
直接使用CDC的例子是內存設備上下文,例如:
CDC dcMem; //聲明一個CDC對象
dcMem.CreateCompatibleDC(&dc); //創建設備描述表
pbmOld = dcMem.SelectObject(&m_bmBall);//更改設備描述表屬性
…//作一些繪製操作
dcMem.SelectObject(pbmOld);//恢復設備描述表的屬性
dcMem.DeleteDC(); //可以不調用,而讓析構函數去刪除設備描述表
系統何時發送WM_PAINT消息?
系統會在多個不同的時機發送WM_PAINT消息:當第一次創建一個窗口時,當改變窗口的大小時,當把窗口從另一個窗口背後移出時,當最大化或最小化窗口時,等等,這些動作都是由 系統管理的,應用只是被動地接收該消息,在消息處理函數中進行繪製操作;大多數的時候應用也需要能夠主動引發窗口中的繪製操作,比如當窗口顯示的數據改變的時候,這一般是通過InvalidateRect和 InvalidateRgn函數來完成的。InvalidateRect和InvalidateRgn把指定的區域加到窗口的Update Region中,當應用的消息隊列沒有其他消息時,如果窗口的Update Region不爲空時,系統就會自動產生WM_PAINT消息。
系統爲什麼不在調用Invalidate時發送WM_PAINT消息呢?又爲什麼非要等應用消息隊列爲空時才發送WM_PAINT消息呢?這是因爲系統把在窗口中的繪製操作當作一種低優先級的操作,於是盡 可能地推後做。不過這樣也有利於提高繪製的效率:兩個WM_PAINT消息之間通過InvalidateRect和InvaliateRgn使之失效的區域就會被累加起來,然後在一個WM_PAINT消息中一次得到 更新,不僅能避免多次重複地更新同一區域,也優化了應用的更新操作。像這種通過InvalidateRect和InvalidateRgn來使窗口區域無效,依賴於系統在合適的時機發送WM_PAINT消息的機 制實際上是一種異步工作方式,也就是說,在無效化窗口區域和發送WM_PAINT消息之間是有延遲的;有時候這種延遲並不是我們希望的,這時我們當然可以在無效化窗口區域後利用SendMessage 發送一條WM_PAINT消息來強制立即重畫,但不如使用Windows GDI爲我們提供的更方便和強大的函數:UpdateWindow和RedrawWindow。UpdateWindow會檢查窗口的Update Region,當其不爲空時才發送WM_PAINT消息;RedrawWindow則給我們更多的控制:是否重畫非客戶區和背景,是否總是發送WM_PAINT消息而不管Update Region是否爲空等。