MFC编程,图形的重绘方法总结

    在上一篇博客——Cview类OnDraw重绘时,视图内容消失问题的解决——中,记录的是自己根据内存缓冲画图的思想编写的重绘图形代码。现在把几种官方的科学且标准的图形重绘方法在此总结。

背景介绍:

如图1所示,工程名为LocateByLh,工具栏上可以选择画线或画点(实际是小实心圆)工具进行绘图,编码的目标是使窗口大小发生改变时,视图内点线图形保持不变(如图二是执行窗口最大化后的效果)。


图 1


图 2

    1、动态数组保存图形要素

    为保存所绘图形的要素(绘图类型<点、线...>,绘图起始坐标点,终点),创建的CGraph类定义如下

class CGraph  {
public:
CGraph(UINT m_nDrawType, CPoint m_ptOrigin, CPoint m_ptEnd);
virtual ~CGraph();

UINT m_nDrawType
;//m_nDrawType=1、2分别代表画点(小实心圆)和画线
CPoint m_ptOrigin;
//绘图时起始坐标点
CPoint m_ptEnd;
//绘图时终点
};

CGraph::CGraph(UINT m_nDrawType, CPoint m_ptOrigin, CPoint m_ptEnd){
this->m_nDrawType=m_nDrawType;
this->m_ptOrigin=m_ptOrigin;
this->m_ptEnd=m_ptEnd;
}
//重写了构造函数

    下面仅主要列出CLocateByLhView类中的OnDraw()、OnLButtonUp()的代码

OnLButtonUp()

void CLocateByLhView::OnLButtonUp(UINT nFlags, CPoint point) {
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);

CBrush brush,*pOldBrush;
CPen pen(PS_SOLID,1,RGB(255,0,0));
//定义一个红色画笔
CPen *pOldPen;
brush.CreateSolidBrush(RGB(255,0,0));
//定义一个红色画刷
switch(m_nDrawType){
             //根据m_DrawType的值判断应该画何种图形
case 1:
 //画点(小实心圆)
pOldPen=dc.SelectObject(&pen);
//选择红色画笔,影响实心圆的边
pOldBrush=dc.SelectObject(&brush);
//选择红色画刷,影响实心圆的填充
dc.Ellipse(CRect(point.x-5,point.y-5,point.x+5,point.y+5));
//画椭圆的绘图函数
break;
case 2:
 //用默认的画笔画线
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
}
CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);
//应使用new开辟堆空间,防止被系统自动释放
m_ptrArray.Add(pGraph);
//使用动态指针数据保存此次绘图的三个要素

CView::OnLButtonUp(nFlags, point);
}

///OnDraw()根据m_ptrArray保存的内容,进行重新绘制

void CLocateByLhView::OnDraw(CDC* pDC)
{
CLocateByLhDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CBrush brush,*pOldBrush;
CPen pen(PS_SOLID,1,RGB(255,0,0));
CPen *pOldPen;
brush.CreateSolidBrush(RGB(255,0,0));
for(int i=0;i<m_ptrArray.GetSize();i++){
switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType){            
case 1:  
pOldPen=pDC->SelectObject(&pen);
pOldBrush=pDC->SelectObject(&brush);
pDC->Ellipse(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd.x-5,((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd.y-5,
((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd.x+5,((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd.y+5));

pDC->SelectObject(pOldPen);//恢复使用前一次的画笔类型,请注意此处这么做是可以的,即在连续用红笔或默认的黑笔都是可行的
pDC->SelectObject(pOldBrush);
//恢复使用前一次的画刷类型
break;
case 2:
pDC->MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin);
pDC->LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd);
break;
}
}
}

2、元文件

  首先介绍一下元文件,可以通过以下步骤使用Windows元文件(引用自《VC++深入详解.孙鑫》)

1)利用CMetaFileDC构造函数构造一个元文件DC对象,然后调用该类的Create成员函数创建一个Windows元文件设备上下文。声明如下所示:

Bool Create(LPCTSTR lpszFilename = NULL);

  其中,参数lpszFilename指定要创建的元文件的文件名,如果为NULL,则所创建的元文件是一个内存元文件;

2)给创建的元文件DC发送GDI命令,即绘图;

3)绘图完成后,调用Close成员函数关闭元文件设备上下文,返回元文件句柄(HMETAFILE类型);

4)以得到的元文件句柄为参数,利用CDC类的PlayMetaFile成员函数播放该元文件。

5)最后需要通过调用DeleteMetaFile成员函数释放元文件资源。

同样,此处主要列出CLocateByLhView类中的OnDraw()、OnLButtonUp()的代码

///OnLButtonUp()主要是以m_dcMetaFile代替方法一中的dc进行绘图

void CLocateByLhView::OnLButtonUp(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
CDC *pDC=GetDC();
CBrush brush;
CPen pen(PS_SOLID,1,RGB(255,0,0));
CPen defaultPen(PS_SOLID,1,RGB(0,0,0));
brush.CreateSolidBrush(RGB(255,0,0));

switch(m_nDrawType){
case 1:
m_dcMetaFile.SelectObject(&pen);
m_dcMetaFile.SelectObject(&brush);
m_dcMetaFile.Ellipse(CRect(point.x-5,point.y-5,point.x+5,point.y+5));
break;
case 2:
m_dcMetaFile.SelectObject(&defaultPen);
//不能采用方法一种的画笔选择方法,m_dcMetaFile调用PlayMetaFile后仅进行绘图操作不会执行绘图后的画笔还原,而画图前的画笔选择则会被执行
m_dcMetaFile.MoveTo(m_ptOrigin);
m_dcMetaFile.LineTo(point);
break;
}
/
/绘图完成后要立即显示出来,注意:下面的六行代码可以直接用Invalidate(true)代替。
HMETAFILE hmetafile;
hmetafile=m_dcMetaFile.Close();
pDC->PlayMetaFile(hmetafile);

m_dcMetaFile.Create();
//m_dcMetaFile的首次创建是在程序启动时首次调用的OnDraw()中
m_dcMetaFile.PlayMetaFile(hmetafile);
//每次创建m_dcMetaFile会将之前的图形清空,此处是为了将之前的所绘图形也保存下来,调用PlayMetaFile相当于一次重新绘制
DeleteMetaFile(hmetafile);

CView::OnLButtonUp(nFlags, point);
}

///OnDraw()

void CLocateByLhView::OnDraw(CDC* pDC)
{
CLocateByLhDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
HMETAFILE hmetafile;
hmetafile=m_dcMetaFile.Close();
pDC->PlayMetaFile(hmetafile);

m_dcMetaFile.Create();
m_dcMetaFile.PlayMetaFile(hmetafile);
DeleteMetaFile(hmetafile);
}

3、兼容设备描述表

先放代码

/OnLButtonUp()

void CLocateByLhView::OnLButtonUp(UINT nFlags, CPoint point) {
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
CBrush brush;
CPen pen(PS_SOLID,1,RGB(255,0,0));
CPen defaultPen(PS_SOLID,1,RGB(0,0,0));
brush.CreateSolidBrush(RGB(255,0,0));

if(!m_dcCompatible.m_hDC){
//未创建则创建该兼容DC对象
m_dcCompatible.CreateCompatibleDC(&dc);
CRect rect;
GetClientRect(&rect);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
m_dcCompatible.SelectObject(&bitmap);
m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);
//关键代码,选择bitmap后,应用BitBlt函数将原始的设备描述表的颜色表及像素块数据复制到兼容设备表中,否则兼容DC呈现的是默认的黑色背景
}

switch(m_nDrawType){
case 1:
m_dcCompatible.SelectObject(&pen);
m_dcCompatible.SelectObject(&brush);
m_dcCompatible.Ellipse(CRect(point.x-5,point.y-5,point.x+5,point.y+5));
break;
case 2:
m_dcCompatible.SelectObject(&defaultPen);
m_dcCompatible.MoveTo(m_ptOrigin);
m_dcCompatible.LineTo(point);
break;
}
Invalidate(true);
CView::OnLButtonUp(nFlags, point);
}

/OnDraw()这个简单。。

void CLocateByLhView::OnDraw(CDC* pDC)
{
CLocateByLhDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CRect rect;
GetClientRect(&rect);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
}

方法三与上篇文章的方法对比:方法三以兼容DC存储图形,上篇文章则是以bitmap来存储;bitmap的初始背景设置不如方法三种对兼容DC调用BitBlt复制颜色表及像素数据;上篇文章还有诸多不成熟之处:如,如何只执行一次对象的创建。 本文中三种方法,仔细分析不难发现其优缺点及适用的场合。

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页