【轉】對話方塊的OnPaint()和OnEraseBkgnd()訊息的理解

來源:互聯網
上載者:User

標籤:

對話方塊的OnPaint()和OnEraseBkgnd()訊息的理解

一個對話方塊重寫OnPaint()和OnEraseBkgnd(),執行發現OnEraseBkgnd()比OnPaint()執行的次數多很多,但是執行OnPaint()前一定會執行OnEraseBkgnd()。項目中用GDI雙緩衝,繪製對話方塊背景圖片的時候,放在OnEraseBkgnd()會出現閃爍,放在OnPaint()裡面就不會閃爍。

這種問題關係到這兩個訊息函數的的理解:特定找了一些資料,以作備查。

 

個人理解:

OnEraseBkgnd()的return CDialogEx::OnEraseBkgnd(pDC),是擦除背景的,如果不想擦除背景,可以位元組return true。

 

OnPaint()觸發的情況:

       1. 移動視窗(movewindow()),視窗被其它視窗遮住的時候,把視窗最小化,再開啟的時候。

       2. 你自己想更新某個地區可以發送Invalidate或InvalidRect,再加上UpdateWindow()也可以觸發。

       注意:在OnPaint()裡面不能執行再次觸發WM_PAINT的語句,不然會死迴圈。

 

以下是別人的理解:摘自http://hi.baidu.com/yang_qi168/item/a648bec135bf1ec8ee183bcc

 

WM_PAINT是視窗每次重繪都會產生的一個訊息。 
OnPaint是對這個訊息的反應函數
mfc 的 CWnd::OnPaint 沒做什麼,只是丟給系統處理。

一 :
先執行OnEraseBkgnd,擦除背景(如果想自繪控制項,這個函數直接return TRUE就可以了,這樣就不會擦除背景,不會閃)


OnEraseBkGnd與OnPaint的區別與聯絡

在OnEraseBkGnd中,如果你不調用原來預設的OnEraseBkGnd只是重畫背景則不會有閃爍.而在OnPaint裡面,由於它隱含的調用了OnEraseBkGnd,而你又沒有處理OnEraseBkGnd 函數,這時就和視窗預設的背景刷相關了.預設的 OnEraseBkGnd操作使用視窗的預設背景刷重新整理背景(一般情況下是白刷),而隨後你又自己重畫背景造成螢幕閃動.

OnEraseBkGnd不是每次都會被調用的.如果你調用Invalidate的時候參數為TRUE,那麼在OnPaint裡面隱含調用BeginPaint的時候就產生WM_ERASEBKGND訊息,如果參數是FALSE,則不會重刷背景.

void Invalidate( BOOL bErase = TRUE ); 該函數的作用是使整個視窗客戶區無效。視窗的客戶區無效意味著需要重繪,參數bErase為TRUE時,重繪地區內的背景將被重繪即擦除,否則,背景將保持不變。調用Invalidate等函數後視窗不會立即重繪,這是由於WM_PAINT訊息的優先順序很低,它需要等訊息佇列中的其它訊息發送完後才能被處理。

OnPaint裡面會調用BeginPaint函數自動化佈建顯示裝置內容的剪下地區而排除任何更新地區外的地區更新地區。如果更新地區被標記為可擦除的,BeginPaint發送一個WM_ERASEBKGND訊息給視窗。WM_ERASEBKGND訊息的響應函數既是OnEraseBkGnd()

所以解決方案有三個半:
1.用OnEraseBkGnd實現,不要調用原來的OnEraseBkGnd函數.
2.用OnPaint實現,同時重載OnEraseBkGnd,其中直接返回.
3.用OnPaint實現,建立視窗時設定背景刷為空白
4.用OnPaint實現,但是要求重新整理時用Invalidate(FALSE)這樣
的函數.(不過這種情況下,視窗覆蓋等造成的重新整理還是要閃一
下,所以不是徹底的解決方案)
都挺簡單的.
在MFC中 任何一個window元件的繪圖 都是放在這兩個member function中
在設定上 OnEraseBkgnd()是用來畫底圖的 而OnPaint()是用來畫主要物件的
舉例說明 一個按鈕是灰色的 上面還有文字
則OnEraseBkgnd()所做的事就是把按鈕畫成灰色
而OnPaint()所做的事 就是畫上文字


既然這兩個member function都是用來畫出元件的
那為何還要分OnPaint() 與 OnEraseBkgnd() 呢
其實OnPaint() 與 OnEraseBkgnd() 特性是有差的
1. OnEraseBkgnd()的要求是快速在裡面的繪圖程式最好是不要太耗時間
因為每當window元件有任何小變動都會馬上呼叫OnEraseBkgnd()
2. OnPaint() 是只有在程式有閒置的時候才會被呼叫
3. OnEraseBkgnd() 是在 OnPaint() 之前呼叫的
所以 OnPaint()被呼叫一次之前 可能會呼叫OnEraseBkgnd()好幾次

如果我們是一個在做圖形化使用者介面的人
常會需要把一張圖片設為我們dialog的背景
把繪圖的代碼放在OnPaint() 之中 可能會常碰到一些問題
比方說拖拽一個視窗在我們做的dialog上面一直移動
則dialog會變成灰色 直到動作停止才恢複
這是因為每次需要重繪的時候 程式都會馬上呼叫OnEraseBkgnd()
OnEraseBkgnd()就把dialog畫成灰色
而只有動作停止之後 程式才會呼叫OnPaint() 這時才會把我們要畫的背景貼上去

這個問題的解法 比較差勁的方法是把OnEraseBkgnd() 改寫成不做事的function
如下所示

BOOL CMyDlg::OnEraseBkgnd(CDC* pDC){  return TRUE;}


以上本來是會調用CDialog::OnEraseBkgnd() 但是如果我們不調用的話
程式便不會畫上灰色的背景了

Q:基於對話方塊的程式中如何重載OnEraseBkGnd()函數

A:這是一個訊息WM_ERASEBKWND 
在CLASS WIZARD中 
選擇CLASSINFO頁面 
在MESSAGEFILTER中的選項設在WINDOW就可以看到這個訊息了.

比較好的做法是直接將繪圖的程式從OnPaint()移到OnEraseBkgnd()來做
如下所示

// m_bmpBKGND 為一CBitmap物件 且事先早已載入我們的底圖
// 底圖的大小與我們的視窗client大小一致

BOOL CMyDlg::OnEraseBkgnd(CDC* pDC){    CRect rc;    GetUpdateRect(&rc);    CDC srcDC;    srcDC.CreateCompatibleDC(pDC);    srcDC.SelectObject(m_bmpBKGND);    pDC->BitBlt(rc.left,rc.top,rc.GetWidth(),    rc.GetHeight(),&srcDC,rc.left,rc.top,SRCCOPY);    return TRUE;}

特別要注意的是 取得重畫大小是使用GetUpdateRect() 而不是GetClientRect()
如果使用GetClientRect() 會把不該重畫的地方重畫

【轉】對話方塊的OnPaint()和OnEraseBkgnd()訊息的理解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.