一个对话框重写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() 會把不該重畫的地方重畫