当窗口无效时,Windows会给窗口发出WM_ERASEBKGND消息和WM_PAINT消息,而且WM_ERASEBKGND消息先发出一次或者几次,紧接着是WM_PAINT消息。有一个例外:InvalidateRect()函数的调用会使窗口变得无效,并产生WM_ERASEBKGND消息和WM_PAINT消息,而WM_ERASEBKGND是否产生取决于参数bErase。WM_ERASEBKGND消息响应函数为OnEraseBkgnd(), WM_PAINT消息响应函数为OnPaint()。
窗口重绘时,先调用OnEraseBkGnd()擦除窗口的现有内容,再调用OnPaint绘制新内容。
问题是这样产生的,缺省的OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情况下是白刷),而你又自己重画背景的话会造成窗口闪动。在OnEraseBkGnd中,如果你不调用原来缺省的OnEraseBkGnd只是重画背景则不会有闪动,而在OnPaint里面,由于它隐含的调用了OnEraseBkGnd,而你又没有处理OnEraseBkGnd 函数,这时就和窗口缺省的背景刷相关了。OnEraseBkGnd不是每次都会被调用的。如果你调用Invalidate的时候参数为TRUE,那么在OnPaint里面隐含调用BeginPaint的时候就产生WM_ERASEBKGND消息,如果参数是FALSE,则不会重刷背景。 所以解决闪动方法有三个:
1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.
2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回.
3.用OnPaint实现,创建窗口时设置背景刷为空
在MFC中 任何一个window组件的绘图 都是放在OnEraseBkGnd()和OnPaint这两个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() 會把不該重畫的地方重畫