问题描述:在进行MFC绘图编程时(以简单的绘制线条为例),视图窗口的大小变化会引起调用OnDraw()。默认的OnDraw()会重绘显示出一个空白的视图,因此必须重写这个方法以显示文档的视图。
解决思路:通过查阅资料发现,这种问题通常的处理方法是在文档中记录绘图数据,在窗口重绘时使用这些数据恢复图像。我认为使用这种方法时,在绘图的同时考虑记录数据将增加不少代码复杂度(因我了解的不够及水平有限,这种观点可能有误),同时在复杂的图像所需要记录的数据量将很大。另外,还有一种“内存缓存画图”的方式,考虑到MFC时 文档/视图 类,视图CView负责数据的显示和修改,文档CDocument类负责数据的存储和加载,从而把数据管理和显示方法分离开来。我们在CDocument类中添加一个CBitMap对象,将每次中间绘图时的客户区的内容保存成BitMap,当所有的操作都执行好以后,将最终的BitMap拷贝到屏幕中,这就是所谓的内存缓存画图方式。我比较倾向于用这种方式解决重绘问题,但我所查到的网上所提供的代码都只能解决绘图时的“闪烁”问题(参考搜索“C++双缓冲绘图”)。因此我基于后者的缓冲绘图思想,以绘制线条为例,编写代码解决重绘问题。
具体实现过程及代码如下(工程名为LocateByLh):
1、在CLocateByLhDoc类中创建CBitmap类型的m_bmpBuf用于保存视图(同时也可以用于缓冲绘图),同时在CLocateByLhView类中添加一个bool型变量。
public:
CBitmap m_bmpBuf;
/
private:
bool bFlag;
/
2、在OnDraw()方法内添加m_bmpBuf输出代码和只在首次执行的m_bmpBuf初始化代码(设置bFlag就是为了只执行一次)。
void CLocateByLhView::OnDraw(CDC* pDC){
CLocateByLhDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
//重绘显示bitmap,思路是创建一个临时的dcMem,先把m_bmpBuf作为画布放入其中,然后拷贝到pDC中显示出来
CDC dcMem;
CRect rect;
GetClientRect(&rect);
dcMem.CreateCompatibleDC(NULL);
dcMem.SelectObject(&pDoc->m_bmpBuf);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);
dcMem.DeleteDC();
//初始化一个白色填充的m_bmpBuf,
if(bFlag){
CLocateByLhDoc *pDoc = GetDocument();
CDC dcMem2;
dcMem2.CreateCompatibleDC(NULL);
pDoc->m_bmpBuf.CreateCompatibleBitmap(pDC,2500,1500);
dcMem2.SelectObject(&pDoc->m_bmpBuf);
dcMem2.FillSolidRect(0,0,2500,1500,RGB(255,255,255));
dcMem2.BitBlt(0,0,rect.Width(),rect.Height(),pDC,0,0,SRCCOPY);
dcMem2.DeleteDC();
bFlag=false;
}
}
3、鼠标左键弹起事件中的绘图线条代码,
void CLocateByLhView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CLocateByLhDoc *pDoc = GetDocument();
CDC* pDC = GetDC();
CDC dcMem;
CRect rect;
GetClientRect(&rect);
dcMem.CreateCompatibleDC(NULL);
//注释部分采用的是非缓冲画图,直接用pDC进行绘图,然后复制到m_bmpBuf
//CPen pen(PS_SOLID,2,RGB(255,0,0));
//pDC->SelectObject(&pen);
//pDC->MoveTo(m_ptOrigin);
//pDC->LineTo(point);
//dcMem.SelectObject(&pDoc->m_bmpBuf);
//dcMem.BitBlt(0,0,rect.Width(),rect.Height(),pDC,0,0,SRCCOPY);
//在虚拟的dcMem中绘图,然后一次性输出显示,同时更新了m_bmpBuf
dcMem.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, 0, 0, SRCCOPY);
dcMem.SelectObject(&pDoc->m_bmpBuf);
dcMem.MoveTo(m_ptOrigin);//绘制线条代码
dcMem.LineTo(point);//绘制线条代码
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);
dcMem.DeleteDC();
ReleaseDC(pDC);
CView::OnLButtonUp(nFlags, point);
}
总结:代码中所涉及的函数如BitBlt,FillSolidRect等很具有特性,对问题的解决造成了很大的困扰,通过在代码的调试中不断的摸索才渐渐熟悉了这些特性,进而解决问题。代码肯定存在不完善的地方,希望大家能给予指正,共同进步!