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复制颜色表及像素数据;上篇文章还有诸多不成熟之处:如,如何只执行一次对象的创建。 本文中三种方法,仔细分析不难发现其优缺点及适用的场合。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
/****************************************************** SolveFlashingAndRedraw框架说明 ******************************************************/ /** 项目名称: SolveFlashingAndRedraw框架 包含文件: 1. ReadMe.txt 2. SolveFlashingAndRedraw MFC工程 版本号: v1.0.1 第一作者: Jef 地址: 中国/江苏 日期: 20091126 电子邮箱: dungeonsnd@126.com QQ: 420554565 (加好友时注明下载的文件名) 版权: 1.您可以修改及免费使用本程序。 2.修改之后附上您的个人信息发送到上面的作者邮箱,作者负责在全面测试后发布您修改后的新版本。 3.您使用本程序而导致任何伤害以及经济损失,由过错方依法承担所有责任,一概与第一作者及合作单位无关。 4.如果您使用本程序则表示您已经同意此版本协议!否则请勿使用! 项目功能: SolveFlashingAndRedraw 样例工程是MFC解决窗口保存及重绘闪烁问题的一种比较好的方案(Win32解决方法类似)。 运行步骤: 直接运行里面的程序,在窗口上任意拖拉鼠标画线,然后点击菜单栏的几个示范菜单项,然后移动窗口、 改变窗口大小、最大最小化窗口、用其它窗口覆盖此窗口、鼠标放到任务栏。。。 以上种种操作观察窗口内的图像变化。可以发现窗口内图像几乎看不到闪烁,而且窗口的元素已经保存下来重绘时任然可以看到图像。 如何使用: 进行项目开发时,可以先建立项目,然后把本解决方案框架拷贝到新建项目中即可。 也可以自己根据需要修改。 其它: 友情提示,小心 View类头文件及View类的实现文件中有说明,使用时别把它弄到你实际项目里哦! 进行大量复杂的图形的输出,而且对效率要求特别高时要考虑适当修改此框架(如增加裁剪区)后再使用哦。 如何有任何问题欢迎与作者分享!!!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值