问题背景:
窗体在重绘时,由于过频的刷新会引起闪烁现象——窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。
绘制过程:
当窗口整体需要重绘时,系统依次发送WM_NCPAINT、WM_ERASEBKGND、WM_PAINT三个独立的消息,即先绘制框架部分,再绘制客户区背景,最后绘制客户区,根据需要也可能只产生其中某一个消息响应。
任何一个window组件的绘图,都是放在OnPaint、OnEraseBkgnd中。在设定上 OnEraseBkgnd是用来画底图的而OnPaint是用来画主要对象的。举例说明:一个按钮是灰色的,上面还有文字,则OnEraseBkgnd所做的事就是把按钮画成灰色,而OnPaint()所做的事就是画上文字。
产生原因:
在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)这样的函数(这种情况下,窗口覆盖等造成的刷新还是要闪一下,所以不是彻底的解决方法)。
思路解决归纳:
比较OnPaint与OnEraseBkgnd:
故使用双缓存:可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去。
综上,采用重载OnEraseBkgnd,通过双缓存来解决画面闪烁问题。
示例代码:
BOOL CSDRClientDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CRect rcClient;
GetClientRect(&rcClient);
BITMAP bmpBKG;
m_cBKGndBmp.GetBitmap(&bmpBKG);
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap *pOldBmp=memDC.SelectObject(&m_cBKGndBmp);
pDC->StretchBlt(0,0,rcClient.Width(),rcClient.Height(),&memDC,0,0,
bmpBKG.bmWidth,bmpBKG.bmHeight,SRCCOPY);
memDC.SelectObject(pOldBmp);
memDC.DeleteDC();
return TRUE;
//return CDialog::OnEraseBkgnd(pDC);
}
窗体在重绘时,由于过频的刷新会引起闪烁现象——窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。
绘制过程:
当窗口整体需要重绘时,系统依次发送WM_NCPAINT、WM_ERASEBKGND、WM_PAINT三个独立的消息,即先绘制框架部分,再绘制客户区背景,最后绘制客户区,根据需要也可能只产生其中某一个消息响应。
任何一个window组件的绘图,都是放在OnPaint、OnEraseBkgnd中。在设定上 OnEraseBkgnd是用来画底图的而OnPaint是用来画主要对象的。举例说明:一个按钮是灰色的,上面还有文字,则OnEraseBkgnd所做的事就是把按钮画成灰色,而OnPaint()所做的事就是画上文字。
产生原因:
在OnEraseBkGnd中,如果不调用原来缺省的OnEraseBkGnd只是重画背景则不会有闪烁。
而在OnPaint里面,由于它隐含的调用了OnEraseBkGnd,如果没有处理OnEraseBkGnd
解决思路:
1.用OnEraseBkGnd实现,不要调用基类的OnEraseBkGnd函数。
2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回。
3.用OnPaint实现,创建窗口时设置背景刷为空(不能将先前的图像擦除)。
4.用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样的函数(这种情况下,窗口覆盖等造成的刷新还是要闪一下,所以不是彻底的解决方法)。
思路解决归纳:
比较OnPaint与OnEraseBkgnd:
1、
2、
3、
在OnPaint之前,可能调用OnEraseBkgnd好几次,所以比较好的方法是OnPaint的方法由OnEraseBkgnd实现。
WM_PAINT消息响应的频度太高,最小化最大化,移动窗体,覆盖等等都引起重绘,很是消耗性能。
故使用双缓存:可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去。
综上,采用重载OnEraseBkgnd,通过双缓存来解决画面闪烁问题。
示例代码:
BOOL CSDRClientDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CRect rcClient;
GetClientRect(&rcClient);
BITMAP bmpBKG;
m_cBKGndBmp.GetBitmap(&bmpBKG);
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap *pOldBmp=memDC.SelectObject(&m_cBKGndBmp);
pDC->StretchBlt(0,0,rcClient.Width(),rcClient.Height(),&memDC,0,0,
memDC.SelectObject(pOldBmp);
memDC.DeleteDC();
return TRUE;