首先说明作图时,会闪烁的原因:
我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来绘制的图形进行清除,而又叠加上了新的图形。
2、如何避免闪烁
在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC提供的背景绘制过程了。实现的方法很多,
* 可以在窗口形成时给窗口的注册类的背景刷付NULL
* 也可以在形成以后修改背景
static CBrush brush(RGB(255,0,0));
SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);
* 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE
这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。
以上方法,自己使用的是重载OnEraseBkgnd 直接返回TRUE, 如此是因为:
如果重载的OnEraseBkgnd在响应WM_ERASEBKGND时处理了这个消息并擦除了背景,则应当返回非零值,表明不需要进一步擦除。如果它返回0,则窗口依然被标记为需要擦除(通常,这意味着PAINTSTRUCT结构的fErase成员将为TRUE)。
由于我实现的是给view一种背景色后,在这之上创建一个画布(实现方法是:添加了一个白色矩形框)来实现绘图。之前view背景色的设置就像上节所说的,重载了OnCtlColor 在其中设置的,但是当在OnEraseBkgnd中return TRUE;之后,就不会在显示背景,那么需要在OnDraw中设置,
起初在OnDraw中是采用了: RECT rect;
GetClientRect(&rect);
pDC->FillSolidRect(0,0,rect.right,rect.bottom,RGB(128,128,128));
然后 之后画布的实现是,用了双缓冲操作。这样写的问题是:背景会闪烁,因此将整个窗体的重绘都写入双缓冲:如下
双缓冲的实现:
CBitmap bmpCanvas; //画布大小
RECT rtClient; //客户窗体大小
GetClientRect(&rtClient);
int iWidth = rtClient.right;
int iHeight = rtClient.bottom;
//如果设置的画布大于窗体
if(m_rtCanvas.right > rtClient.right)
iWidth = m_rtCanvas.right;
if(m_rtCanvas.bottom > rtClient.bottom)
iHeight = m_rtCanvas.bottom;
bmpCanvas.CreateCompatibleBitmap(pDC, iWidth,iHeight);
if(m_dcMemory.m_hDC)
m_dcMemory.Detach();
m_dcMemory.CreateCompatibleDC(pDC); // 创建一个内存设备环境
m_dcMemory.SelectObject(&bmpCanvas); // 将位图选入设备环境
m_dcMemory.FillSolidRect(0,0,iWidth,iHeight,RGB(128,128,128)); //整个客户窗体填充色
m_dcMemory.FillSolidRect(0,0,m_rtCanvas.right,m_rtCanvas.bottom,m_viewBkColor); //在确定的位置显示画布
if(m_bkMap != _T("")) //填充背景图片
{
//根据路径获得图片
CImage image;
image.Load(m_bkMap);
CRect rect;
rect.left = 0;
rect.top = 0;
rect.right = m_rtCanvas.right;
rect.bottom = m_rtCanvas.bottom;
image.Draw(m_dcMemory.m_hDC,rect);
}
GetDocument()->onClientDraw(&m_dcMemory); //在内存m_dcMemory上画各个图元 线、圆、矩形等
pDC->BitBlt(0,0,iWidth,iHeight,&m_dcMemory,0,0,SRCCOPY); //将图在view上显示