天天看點

MFC中的OnDraw和OnPaint的對比

學習中遇到一個問題,OnDraw與OnPaint有什麼差別?上網搜尋了一下,又查了一下MSDN和MFC的一些源檔案,現整理如下。

OnPaint是WM_PAINT消息的消息處理函數,在OnPaint中調用OnDraw,一般來說,使用者自己的繪圖代碼應放在OnDraw中。

OnPaint()是CWnd的類成員,負責響應WM_PAINT消息。OnDraw()是CVIEW的成員函數,沒有響應消息的功能.當視圖變得無效時(包括大小的改變,移動,被遮蓋等等),Windows發送WM_PAINT消息。該視圖的OnPaint 處理函數通過建立CPaintDC類的DC對象來響應該消息并調用視圖的OnDraw成員函數.OnPaint最後也要調用OnDraw,是以一般在OnDraw函數中進行繪制。

The WM_PAINT message is sent when the UpdateWindow or RedrawWindow member function is called.

在OnPaint中,将調用BeginPaint,用來獲得客戶區的顯示裝置環境,并以此調用GDI函數執行繪圖操作。在繪圖操作完成後,将調用EndPaint以釋放顯示裝置環境。而OnDraw在BeginPaint與EndPaint間被調用。

1) 在mfc結構裡OnPaint是CWnd的成員函數. OnDraw是CView的成員函數.

2) OnPaint()調用OnDraw(),OnPrint也會調用OnDraw(),是以OnDraw()是顯示和列印的共同操作。

OnPaint是WM_PAINT消息引發的重繪消息處理函數,在OnPaint中會調用OnDraw來進行繪圖。OnPaint中首先構造一個CPaintDC類得執行個體,然後一這個執行個體為參數來調用虛函數OnPrepareDC來進行一些繪制前的一些處理,比設定映射模式,最後調用OnDraw。而OnDraw和OnPrepareDC不是消息處理函數。是以在不是因為重繪消息所引發的OnPaint導緻OnDraw被調用時,比如在OnLButtonDown等消息處理函數中繪圖時,要先自己調用OnPrepareDC。

至于CPaintDC和CClientDC根本是兩回事情 CPaintDC是一個裝置環境類,在OnPaint中作為參數傳遞給OnPrepareDC來作裝置環境的設定。真正和CClientDC具有可比性的是CWindowDC,他們一個是描述客戶區域,一個是描述整個螢幕。

如果是對CVIEW或從CVIEW類派生的視窗繪圖時應該用OnDraw。

OnDraw()和OnPaint()有什麼差別呢?

首先:我們先要明确CView類派生自CWnd類。而OnPaint()是CWnd的類成員,同時負責響應WM_PAINT消息。OnDraw()是CVIEW的成員函數,并且沒有響應消息的功能。這就是為什麼你用VC成的程式代碼時,在視圖類隻有OnDraw沒有OnPaint的原因。而在基于對話框的程式中,隻有OnPaint。

其次:我們在第《每天跟我學MFC》3的開始部分已經說到了。要想在螢幕上繪圖或顯示圖形,首先需要建立裝置環境DC。其實DC是一個資料結構,它包含輸出裝置(不單指你17寸的純屏顯示器,還包括列印機之類的輸出裝置)的繪圖屬性的描述。MFC提供了CPaintDC類和CWindwoDC類來實時的響應,而CPaintDC支援重畫。當視圖變得無效時(包括大小的改變,移動,被遮蓋等等),Windows 将 WM_PAINT 消息發送給它。該視圖的OnPaint 處理函數通過建立 CPaintDC 類的DC對象來響應該消息并調用視圖的 OnDraw 成員函數。通常我們不必編寫重寫的 OnPaint 處理成員函數。

///CView預設的标準的重畫函數

void CView::OnPaint() //見VIEWCORE.CPP

{

CPaintDC dc(this);

OnPrepareDC(&dc);

OnDraw(&dc); //調用了OnDraw

}

///CView預設的标準的OnPrint函數

void CView::OnPrint(CDC* pDC, CPrintInfo*)

ASSERT_VALID(pDC);

OnDraw(pDC); // Call Draw

既然OnPaint最後也要調用OnDraw,是以我們一般會在OnDraw函數中進行繪制。下面是一個典型的程式。

///視圖中的繪圖代碼首先檢索指向文檔的指針,然後通過DC進行繪圖調用。

void CMyView::OnDraw( CDC* pDC )

CMyDoc* pDoc = GetDocument();

CString s = pDoc->GetData();

GetClientRect( &rect ); // Returns a CString CRect rect;

pDC->SetTextAlign( TA_BASELINE | TA_CENTER );

pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );

最後:現在大家明白這哥倆之間的關系了吧。是以我們一般用OnPaint維護視窗的客戶區(例如我們的視窗客戶區加一個背景圖檔),用OnDraw維護視圖的客戶區(例如我們通過滑鼠在視圖中畫圖)。當然你也可以不按照上面規律來,隻要達到目的并且沒有問題,怎麼幹都成。補充:我們還可以利用Invalidate(),ValidateRgn(),ValidateRect()函數強制的重畫視窗,具體的請參考MSDN吧。

OnDraw中可以繪制使用者區域。OnPaint中隻是當視窗無效時重繪不會保留CClientDC繪制的内容。

這兩個函數有差別也有聯系:

1、差別:OnDraw是一個純虛函數,定義為virtual void OnDraw( CDC* pDC ) = 0; 而OnPaint是一個消息響應函數,它響應了WM_PANIT消息,也是是視窗重繪消息。

2、聯系:我們一般在視類中作圖的時候,往往不直接響應WM_PANIT消息,而是重載OnDraw純虛函數,這是因為在CVIEW類中的WM_PANIT消息響應函數中調用了OnDraw函數,如果在CMYVIEW類中響應了WM_PAINT消息,不顯式地調用OnDraw函數的話,是不會在視窗重繪的時候調用OnDraw函數的。

應用程式中幾乎所有的繪圖都在視圖的 OnDraw 成員函數中發生,必須在視圖類中重寫該成員函數。(滑鼠繪圖是個特例,這在通過視圖解釋使用者輸入中讨論。)

OnDraw 重寫:

通過調用您提供的文檔成員函數擷取資料。

通過調用架構傳遞給 OnDraw 的裝置上下文對象的成員函數來顯示資料。

當文檔的資料以某種方式更改後,必須重繪視圖以反映該更改。預設的 OnUpdate 實作使視圖的整個工作區無效。當視圖變得無效時,Windows 将 WM_PAINT 消息發送給它。該視圖的 OnPaint 處理函數通過建立 CPaintDC 類的裝置上下文對象來響應該消息并調用視圖的 OnDraw 成員函數。

當沒有添加WM_PAINT消息處理時,視窗重繪時,由OnDraw來進行消息響應...當添加WM_PAINT消息處理時,視窗重繪時,WM_PAINT消息被投遞,由OnPaint來進行消息響應.這時就不能隐式調用OnDraw了.必須顯式調用(CDC *pDC=GetDC(); OnDraw(pDC);)..

隐式調用:當由OnPaint來進行消息響應時,系統自動調用CView::OnDraw(&pDC).