现在的位置: 首页 > 综合 > 正文

MFC 图形的重绘与保存 — 总结

2017年12月03日 ⁄ 综合 ⁄ 共 4845字 ⁄ 字号 评论关闭

一、利用集合类CPtrArray实现

1.注意到,绘图时的关键参数有三个:原结点,末结点,绘制样式。
所以我们先添加一个类CGraph,如下:

class CGraph
{
public:
   CGraph(void);
   CGraph(int nDrawType, CPoint& ptOrigin, CPoint& ptEnd);
   ~CGraph(void);
public:
   int  m_nDrawType;
   CPoint m_ptOrigin;
   CPoint m_ptEnd;
};

 

2.其中CGraph(int nDrawType, CPoint& ptOrigin, CPoint& ptEnd)构造函数是我们主要用到的构造方式,
我们提供这三个参数两构造具体的CGraph类对象。其代码如下:

CGraph::CGraph(int nDrawType, CPoint& ptOrigin, CPoint& ptEnd)
{
   m_nDrawType = nDrawType;
   m_ptOrigin  = ptOrigin;
   m_ptEnd     = ptEnd;
}

3.在视图类CXXXView 中包含刚定义的CGraph类头文件:#include "Graph.h"
并为视图类CXXXView 添加一个CPtrArray类的对象,作为成员变量。

4.上面都是完成重绘功能的准备工作,下面我们来具体完成重绘操作:

在OnLButtonDown这个响应函数中,根据具体的m_nDrawType,m_ptOrigin,m_ptEnd  参数来构造一个CGrahp对象,
再利用CPtrArray 类的Add成员函数将这个对象添加到CPtrArray中。
有一点要注意,CGraph应选择new 来构造,只是为了防止局部对象在退出其作用域后被销毁所引起的错误。

代码如下:

 CGraph *pGraph = new CGraph(m_nDrawType, m_ptOrigin, point);
 m_ptrArray.Add(pGraph);

在重绘发生时,我们依次绘制CPtrArray中的每一个CGraph对象。
代码如下:

   int n = m_ptrArray.GetSize();
   for (int i = 0; i < n; i++)
   {
       switch (((CGraph*)(m_ptrArray.GetAt(i)))->m_nDrawType)
       {
          case DRAW_DOT:
              pDC->SetPixel(((CGraph*)(m_ptrArray.GetAt(i)))->m_ptEnd, RGB(255, 0, 0));
              break;
          case DRAW_LINE:
              pDC->MoveTo(((CGraph*)(m_ptrArray.GetAt(i)))->m_ptOrigin);
              pDC->LineTo(((CGraph*)(m_ptrArray.GetAt(i)))->m_ptEnd);
              break;
          case DRAW_RECTANGLE:
              pDC->Rectangle(((CGraph*)(m_ptrArray.GetAt(i)))->m_ptOrigin.x, ((CGraph*)(m_ptrArray.GetAt(i)))->m_ptOrigin.y,

                            ((CGraph*)(m_ptrArray.GetAt(i)))->m_ptEnd.x, ((CGraph*)(m_ptrArray.GetAt(i)))->m_ptEnd.y);
              break;
          case DRAW_ELLIPSE:
              pDC->Ellipse(((CGraph*)(m_ptrArray.GetAt(i)))->m_ptOrigin.x, ((CGraph*)(m_ptrArray.GetAt(i)))->m_ptOrigin.y,

                          ((CGraph*)(m_ptrArray.GetAt(i)))->m_ptEnd.x, ((CGraph*)(m_ptrArray.GetAt(i)))->m_ptEnd.y);
              break;
         default:
              break;
       }
   }

 

二、利用元文件DC类CMetaFileDC实现

1.在视图类CXXXView 中添加一个CMetaFileDC类的对象,作为成员变量,
并在其构造函数中调用CMetaFileDC类的成员函数Create创建一个元文件设备环境。

CXXXView 头文件:
private:
    CMetaFileDC m_dcMetaFile;

CXXXView 源文件构造函数:
    m_dcMetaFile.Create();

2.在视图类的OnLButtonUp响应函数中,每当向客户区DC绘制一个图形的同时,也将该图形绘制命令发送给元文件对象m_dcMetaFile。
代码如下:

   CClientDC dc(this);
   switch (m_nDrawType)
  {
      case DRAW_DOT:
          dc.SetPixel(point, RGB(255, 0, 0));
          m_dcMetaFile.SetPixel(point, RGB(255, 0, 0));
          break;
 
      case DRAW_LINE:
          dc.MoveTo(m_ptOrigin);
          dc.LineTo(point);
          m_dcMetaFile.MoveTo(m_ptOrigin);
          m_dcMetaFile.LineTo(point);
          break;

      case DRAW_RECTANGLE:
          dc.Rectangle(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
          m_dcMetaFile.Rectangle(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
          break;

      case DRAW_ELLIPSE:
          dc.Ellipse(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
          m_dcMetaFile.Ellipse(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
          break;

      default:
          break;
   }

3.当发生重绘操作时,可通过播放元文件对象m_dcMetaFile中保存的图形绘制命令,使图形实现重绘:
代码如下:

 HMETAFILE hmetaFile;
 hmetaFile = m_dcMetaFile.Close();

 pDC->PlayMetaFile(hmetaFile);

 m_dcMetaFile.Create();
 m_dcMetaFile.PlayMetaFile(hmetaFile);
 DeleteMetaFile(hmetaFile);

至此,我们已完成了图形的重绘工作。

另附,元文件的保存与打开操作:

元文件的保存:

   HMETAFILE hmetaFile;
   hmetaFile = m_dcMetaFile.Close();

   CopyMetaFile(hmetaFile, TEXT("我的元文件.wmf"));

   m_dcMetaFile.Create();
   DeleteMetaFile(hmetaFile);

元文件的打开:

   HMETAFILE hmetaFile;
   hmetaFile = GetMetaFile(TEXT("我的元文件.wmf"));

   m_dcMetaFile.PlayMetaFile(hmetaFile);

   DeleteMetaFile(hmetaFile);
   Invalidate();

 

 

三、利用兼容设备环境实现

1.在视图类CXXXView 中添加一个CDC类对象,作为成员变量。

2.在OnLButtonUp响应函数中,将之前定义的CDC类对象创建为当前设备环境DC的一个兼容设备环境。
实现代码如下:

if (!m_dcCompatible.m_hDC)
{
    m_dcCompatible.CreateCompatibleDC(&dc);

    CRect rect;
    CBitmap bitmap;
    GetClientRect(&rect);
    bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());

    m_dcCompatible.SelectObject(&bitmap);
    m_dcCompatible.BitBlt(0, 0, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY);
}

3.在之后的每一句绘图语句之后,同时将绘图操作应用于兼容设备环境。也即是说,在内存中有当前位图的一份备份。
实现代码如下:

switch (m_nDrawType)
{
 case DRAW_DOT:
     dc.SetPixel(point, RGB(255, 0, 0));
     m_dcCompatible.SetPixel(point, RGB(255, 0, 0));
     break;
 case DRAW_LINE:
     dc.MoveTo(m_ptOrigin);
     dc.LineTo(point);
     m_dcCompatible.MoveTo(m_ptOrigin);
     m_dcCompatible.LineTo(point);
     break;
 case DRAW_RECTANGLE:
     dc.Rectangle(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
     m_dcCompatible.Rectangle(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
     break;
 case DRAW_ELLIPSE:
     dc.Ellipse(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
     m_dcCompatible.Ellipse(m_ptOrigin.x, m_ptOrigin.y, point.x, point.y);
     break;
 default:
     break;
 }

4.在OnDraw响应函数中实现重绘操作。

如下:

   CRect rect;
   GetClientRect(&rect);
   pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcCompatible, 0, 0, SRCCOPY);

有一点需要注意,如下在创建兼容设备环境时,一定要留意到其选择的位图大小。
在创建时位图大小与当前客户区大小是一致的,所在我们创建的兼容设备环境大小与当前客户区大小一致并保持不变。
而程序运行时,客户区大小是可变的,如果客户区大小比兼容设备环境要大,那有一部分的绘图操作所绘制的图形在客户区上将得不到显示。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

抱歉!评论已关闭.