CPaintDC, CClientDC與CWindowDC的區別

     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是否爲空等。

發佈了30 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章