關於OnEraseBkgnd和OnPaint的轉載

RascallySnake發表於2013-10-29

問題是這樣產生的.在OnEraseBkGnd中,如果你不呼叫原來預設的OnEraseBkGnd只是重畫背景則不會有閃爍.而在OnPaint裡面,
由於它隱含的呼叫了OnEraseBkGnd,而你又沒有處理OnEraseBkGnd 函式,這時就和視窗預設的背景刷相關了.預設的 OnEraseBkGnd操作使用視窗的預設背景刷重新整理背景(一般情況 下是白刷),而隨後你又自己重畫背景造成螢幕閃動. 另外一個問題是OnEraseBkGnd不是每次都會被呼叫的.如果你呼叫Invalidate的時候引數為TRUE,那麼在OnPaint裡面隱含 呼叫BeginPaint的時候就產生WM_ERASEBKGND訊息,如果引數是FALSE,則不會重刷背景.
 
所以解決方法有三個半:
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() 但是如果我們不呼叫的話,程式便不會畫上灰色的底色了


比較好的做法是直接將繪圖的程式從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() 會把不該重畫的地方重畫.

相關文章