解决这一问题的办法依然是使用双缓冲,和上次不同的上,用于缓冲的内存DC和位图要保持住,而非随用随建。为了解决第一点不足,需要建立一个内存DC和位图,并在OnDraw过程中将其拷贝到前台。这样欧文们作图的时候将图做在内存DC上,然后使窗口刷新,便看到了所做的图了。当窗体移动或被遮挡时,窗口需要重绘,只是把我们的缓冲重新绘制一遍,因此已经画好的线不会被擦除。解决第二的问题的方法主要有两种,一种是设置画笔的模式为异或模式,这样的话在同意位置画两次的话等同于什么都没画。但是这样会牺牲线条本身的颜色属性,如果背景是单一的颜色,自然是看不出来的,但是如果背景颜色丰富,那我们的线条也就随着多姿多彩了;第二种方法是比较好用的脏矩形法,脏矩形法的主要内容就是每次画面的刷新只更新需要更新的那一块区域,这正是Flash采用的方式,效率比较高。在本程序中为简单起见,没有采用完全的脏矩形法,脏矩形始终定义为整个客户区大小。为此,需要重新定义一个新的缓冲区,用来保存即将变脏的矩形,以备之后恢复所用。在响应WM_MOUSEMOVE消息的过程中,先将新缓冲区上的图形还原到旧缓冲区上,这样可以遮挡住上次的线条。再在旧缓冲区上作图,然后刷新窗口,我们便看到了动态效果。不知道这是不是传说中的三缓冲技术呢?
以下是代码的分析:
CBitmap memBakBMP; //新缓冲区内存DCCDC memBakDC; //新缓冲区用的位图BOOL bClicked; //判断是否应当作图CPoint ptBegin; //记录图象开始的位置CBitmap memBMP; //新缓冲区用的位图CDC memDC; //旧缓冲区内存DC
响应WM_CREATE消息的时候做初始化工作:
int CNSSDrawerView::OnCreate(LPCREATESTRUCT lpCreateStruct){if (CView::OnCreate(lpCreateStruct) == -1) return -1;memDC.CreateCompatibleDC(NULL);memBMP.CreateCompatibleBitmap(&memDC,1024,768);memDC.SelectObject(&memBMP);memDC.FillSolidRect(0,0,1024,768,GetDC()->GetBkColor());memBakDC.CreateCompatibleDC(NULL);memBakBMP.CreateCompatibleBitmap(&memBakDC,1024,768);memBakDC.SelectObject(&memBakBMP);return 0;}
相应的,在响应WM_DESTROY消息的时候,也要做善后工作:
void CNSSDrawerView::OnDestroy(){CView::OnDestroy();memDC.DeleteDC();memBMP.DeleteObject();memBakDC.DeleteDC();memBakBMP.DeleteObject();}
干预WM_ERASEBKGND消息的响应,这里是最容易忽略的地方,要特别注意
BOOL CNSSDrawerView::OnEraseBkgnd(CDC* pDC){return TRUE; //CView::OnEraseBkgnd(pDC);}
部分变量在构造函数中初始化:
CNSSDrawerView::CNSSDrawerView(){ptBegin = 0;bClicked = FALSE;}
响应WM_LBUTTONDOWN消息,着是画线的开始:
void CNSSDrawerView::OnLButtonDown(UINT nFlags, CPoint point){ptBegin = point;bClicked = TRUE;CRect rect;GetClientRect(&rect);memBakDC.BitBlt(0,0,rect.Width(),rect.Height(),&memDC,0,0,SRCCOPY);CView::OnLButtonDown(nFlags, point);}
相应的,WM_LBUTTONUP消息的响应是画线的结束:
void CNSSDrawerView::OnLButtonUp(UINT nFlags, CPoint point){bClicked = FALSE;CView::OnLButtonUp(nFlags, point);}
最主要的部分在响应WM_MOUSEMOVE消息的模块中:
void CNSSDrawerView::OnMouseMove(UINT nFlags, CPoint point){if(bClicked){ CRect rect; GetClientRect(&rect); memDC.BitBlt(0,0,rect.Width(),rect.Height(),&memBakDC,0,0,SRCCOPY); memDC.MoveTo(ptBegin); memDC.LineTo(point); Invalidate();}CView::OnMouseMove(nFlags, point);}