最近用VC做了一个画图的控件。控件在使用的时候遇到点问题。在控件里画了图之后切换到其他页面,等再切换回来的时候,发现控件里画的图都不见了。这是因为VC里面,当缩小、遮挡页面后客户区域就会失效,当再次显示的时候系统就自动调用OnDraw 函数进行重绘。所以原来保存的图形都消失了。由于我做的是控件,所以不可能用一般的方法来解决比如在OnDraw 函数里绘图。经过这种查找决定使用双缓冲绘图来解决这个问题。
普通的绘图是直接将图像绘制到设备上,双缓冲绘图是将图形绘制到内存的一张图片上,等所有的绘图完成后再将整幅图片显示在设备上(个人的理解可能 不太准确)。所以这里我们可以将图片设为成员变量。各绘图函数都在该图片上绘图。并且在OnDraw 函数里显示这幅图片,这样就可以在页面切换回来的时候显示以前画的图了。
代码如下:
文件DrawShapeCtrl.h 中:
class CDrawShapeCtrl : public COleControl
{
........
private:
CBitmap m_Bitmap;
//CDC m_dcMem;//切记不能这样定义
BOOL m_bFisrtTime;//第一次加载
BOOL m_bClear;
};
DrawShapeCtrl.cpp 中:
CDrawShapeCtrl::CDrawShapeCtrl()
{
InitializeIIDs(&IID_DDrawShape, &IID_DDrawShapeEvents);
// TODO: Initialize your control's instance data here.
m_bFisrtTime = TRUE;
m_bClear = FALSE;
}
OnDraw 函数中:
void CDrawShapeCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
if (!pdc)
return;
//// TODO: Replace the following code with your own drawing code.
CRect rect;
GetClientRect(&rect);
CDC dcMem;
dcMem.CreateCompatibleDC(pdc);
//first time
if (m_bFisrtTime)
{
m_Bitmap.CreateCompatibleBitmap(pdc,rect.Width(),rect.Height());
dcMem.SelectObject(&m_Bitmap);
//填充为白色
dcMem.FillSolidRect(0,0,rect.Width(),rect.Height(),RGB(255,255,255));
//画坐标轴
CPen pen;
CPen* pOldPen;
pen.CreatePen( PS_DOT, 1, RGB(120,120,120));
pOldPen = dcMem.SelectObject( &pen );
dcMem.MoveTo(rect.left,rect.Height()/2);
dcMem.LineTo(rect.right,rect.Height()/2);
dcMem.MoveTo(rect.Width()/2,rect.top);
dcMem.LineTo(rect.Width()/2,rect.bottom);
dcMem.SelectObject(pOldPen);
pOldPen->DeleteObject();
m_bFisrtTime = FALSE;
}
dcMem.SelectObject(&m_Bitmap);
if (m_bClear)
{
//填充为白色
dcMem.FillSolidRect(0,0,rect.Width(),rect.Height(),RGB(255,255,255));
//画坐标轴
CPen pen;
CPen* pOldPen;
pen.CreatePen( PS_DOT, 1, RGB(120,120,120));
pOldPen = dcMem.SelectObject( &pen );
dcMem.MoveTo(rect.left,rect.Height()/2);
dcMem.LineTo(rect.right,rect.Height()/2);
dcMem.MoveTo(rect.Width()/2,rect.top);
dcMem.LineTo(rect.Width()/2,rect.bottom);
dcMem.SelectObject(pOldPen);
pOldPen->DeleteObject();
m_bClear = FALSE;
}
//显示
pdc->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);
dcMem.DeleteDC();
}
画线的函数:
//Lstype--penstyle 线型 Lwidth --线宽度 color--颜色
void CDrawShapeCtrl::Line(LONG x1, LONG y1, LONG x2, LONG y2,LONG Lstyle,LONG Lwidth,OLE_COLOR color)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
CDC *pdc = GetDC();
CRect rcClient;
GetClientRect(rcClient);
CDC memDC;
memDC.CreateCompatibleDC(pdc);
memDC.SelectObject(&m_Bitmap);
// Change map mode, positive x right, positive y up.
int nOldMode = memDC.SaveDC();
memDC.SetMapMode(MM_ISOTROPIC);
memDC.SetViewportExt(1, 1);
memDC.SetWindowExt(1, -1);
memDC.SetViewportOrg(rcClient.Width() / 2, rcClient.Height()/2);
//设置画笔线型 宽度 颜色
LOGBRUSH logBrush;
logBrush.lbStyle = BS_SOLID;
logBrush.lbColor = color;
if (Lstyle<0||Lstyle>4)
{
return;
}
CPen Pen(Lstyle|PS_GEOMETRIC|PS_ENDCAP_ROUND, Lwidth,&logBrush);
memDC.SelectObject(&Pen);
//画线
memDC.MoveTo(x1,y1);
memDC.LineTo(x2,y2);
// We must restore mapping mode before copy bitmap to client context.
memDC.RestoreDC(nOldMode);
// Copy double buffer bitmap to client context.
pdc->BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &memDC, 0, 0, SRCCOPY);
ReleaseDC(pdc);
memDC.DeleteDC();
}
清空控件内的图形:
void CDrawShapeCtrl::Clear(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your dispatch handler code here
m_bClear = TRUE;
Invalidate(TRUE);
}
在使用的过程当中发现,CDC 不能定义为全局的,必须定义为局部变量,用完随即释放。否则第一次能画出图形,第二次调用的时候什么也画不出来。原因参考
https://wenku.baidu.com/view/2c6aaf1ba8114431b90dd862.html
参考:
https://blog.csdn.net/imletterh/article/details/46372753