WM_ERASEBKGND

当窗口整体需要重绘时,系统依次发送WM_NCPAINT, WM_ERASEBKGND, WM_PAINT三个独立的消息
即先绘制框架部分,再绘制客户区背景,最后绘制客户区。

在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() 会把不该重画的地方重画。

你可能感兴趣的:(WM_ERASEBKGND)