1. 程式人生 > >MFC中OnPaint()和OnDraw()

MFC中OnPaint()和OnDraw()

在檢視畫的圖象或者文字,當視窗改變後為什麼不見了?OnDraw()和OnPaint()兩個都是解決上面的問題,有什麼不同?(引用自百度)
OnDraw()和OnPaint()好象兄弟倆,因為它們的工作類似。


至於不見了的問題簡單,因為當你的視窗改變後,會產生無效區域,這個無效的區域需要重畫。一般Windows回傳送兩個訊息WM_PAINT(通知客戶區有變化)和WM_NCPAINT(通知非客戶區有變化)。非客戶區的重畫系統自己搞定了,而客戶區的重畫需要我們自己來完成。這就需要 OnDraw()或OnPaint()來重畫視窗。

OnDraw()和OnPaint()有什麼區別呢?
首先:
我們先要明確CView類派生自CWnd類。而OnPaint()是CWnd的類成員,同時負責響應WM_PAINT訊息。OnDraw()是CVIEW 的成員函式,並且沒有響應訊息的功能。這就是為什麼你用VC的程式程式碼時,在檢視類只有OnDraw沒有OnPaint的原因。

其次:
要想在螢幕上繪圖或顯示圖形,首先需要建立裝置環境DC。其實DC是一個數據結構,它包含輸出裝置(不單指你17寸的純屏顯示器,還包括印表機之類的輸出裝置)的繪圖屬性的描述。MFC提供了CPaintDC類和CWindwoDC類來實時的響應,而 CPaintDC支援重畫。

當檢視變得無效時(包括大小的改變,移動,被遮蓋等等),Windows 將 WM_PAINT 訊息傳送給它。該檢視的 OnPaint 處理函式通過建立CPaintDC 類的DC物件來響應該訊息並呼叫檢視的 OnDraw 成員函式。通常我們不必編寫重寫的 OnPaint 處理成員函式。

///CView預設的標準的重畫函式
void CView::OnPaint()
{
 CPaintDC dc(this);
 OnPreparDC(&dc);
 OnDraw(&dc); //呼叫了OnDraw
}

既然OnPaint最後也要呼叫OnDraw,因此我們一般會在OnDraw函式中進行繪製。下面是一個典型的程式

///檢視中的繪圖程式碼首先檢索指向文件的指標,然後通過DC進行繪圖呼叫。
void CMyView::OnDraw( CDC* pDC )
{
 CMyDoc* pDoc = GetDocument();
 CString s = pDoc->GetData(); // Returns a CString
 CRect rect;

 GetClientRect( &rect );
 pDC->SetTextAlign( TA_baseLINE | TA_CENTER );
 pDC->TextOut( rect.right / 2, rect.bottom / 2,
 s, s.GetLength() );
}

最後:
現在大家明白這哥倆之間的關係了吧。因此我們一般用OnPaint維護視窗的客戶區(例如我們的視窗客戶區加一個背景圖片),用OnDraw維護檢視的客戶區(例如我們通過滑鼠在檢視中畫圖)。當然你也可以不按照上面規律來,只要達到目的並且沒有問題,怎麼幹都成。

補充:
我們還可以利用Invalidate(),ValidateRgn(),ValidateRect()函式強制的重畫視窗,具體的請參考MSDN。 

下面是WinAPI(來自MSDN)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
關於OnPaint()函式介面:CWnd::OnPaint

The framework calls this member function when Windows or an application makes a request to repaint a portion of an application's window.

afx_msg void OnPaint( );

Remarks

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

A window may receive internal paint messages as a result of calling the RedrawWindow

member function with the RDW_INTERNALPAINT flag set. In this case, the window may not have an update region. An application should call the GetUpdateRect member function to determine whether the window has an update region. If GetUpdateRect returns 0, the application should not call the BeginPaint and EndPaint member functions.

It is an application's responsibility to check for any necessary internal repainting or updating by looking at its internal data structures for each WM_PAINT message because a WM_PAINT message may have been caused by both an invalid area and a call to the RedrawWindow member function with the RDW_INTERNALPAINT flag set.

An internal WM_PAINT message is sent only once by Windows. After an internal WM_PAINT message is sent to a window by the UpdateWindow member function, no further WM_PAINT messages will be sent or posted until the window is invalidated or until the RedrawWindow member function is called again with the RDW_INTERNALPAINT flag set.

For information on rendering an image in document/view applications, see CView::OnDraw.

For more information about using WM_Paint, see the following topics in the Windows SDK: 

  • The WM_PAINT Message

  • Using the WM_PAINT Message

Requirements

Header: afxwin.h

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
關於OnDraw()函式:CView::OnDraw

Called by the framework to render an image of the document.

virtual void OnDraw( CDC* pDC ) = 0;

Parameters

Remarks

The framework calls this function to perform screen display, printing, and print preview, and it passes a different device context in each case. There is no default implementation.

You must override this function to display your view of the document. You can make graphic device interface (GDI) calls using the CDC object pointed to by the pDC parameter. You can select GDI resources, such as pens or fonts, into the device context before drawing and then deselect them afterwards. Often your drawing code can be device-independent; that is, it doesn't require information about what type of device is displaying the image.

To optimize drawing, call the RectVisible member function of the device context to find out whether a given rectangle will be drawn. If you need to distinguish between normal screen display and printing, call the IsPrinting member function of the device context.