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

序列化–画图问题

2013年03月19日 ⁄ 综合 ⁄ 共 5727字 ⁄ 字号 评论关闭

 

通过存档存储及加载 CObject (见前)

下面用一个示例来解释这个问题。

目标:一个画图程序,通过保存打开按钮存取图片。方法:保存图片绘制信息。

按步骤:

l         创建可序列化的类 ->Graph.cpp+Graph.h

l         在 View 类中添加对控件的响应,实现画图功能,每次鼠标弹起的时候保存绘图信息

l         保存文件(通过 Doc 的 Serialize 来保存数据)

l         打开文件(通过 Doc 的 Serialize 来读取数据,并将其重绘)

在 View 类中定义 CObArray m_obArray;

下面按这个思路来完成:(在 C Graph 子类中完成重绘的画图功能)

Graph.cpp

#include "StdAfx.h"

#include "./graph.h"

IMPLEMENT_SERIAL(CGraph, CObject, 1 )

CGraph::CGraph(void)

: m_ptOrigin(0)

, m_ptEnd(0)

, m_nDrawType(0)

{

}

CGraph::CGraph(CPoint m_ptOrigin,CPoint m_ptEnd,UINT m_nDrawType)

{

     this->m_ptOrigin=m_ptOrigin;

     this->m_ptEnd=m_ptEnd;

     this->m_nDrawType=m_nDrawType;

}

CGraph::~CGraph(void)

{

}

void CGraph::Serialize( CArchive& ar )

{

     //
继承基类的CObject

    CObject::Serialize( ar );

    if( ar.IsStoring() )

     {

        ar << m_ptOrigin << m_ptEnd << m_nDrawType ;

     }

     else

     {

         ar >> m_ptOrigin >> m_ptEnd >> m_nDrawType ;

     }

}

void CGraph::Draw(CDC* pDC)

{

     CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

     CBrush *pOldBrush=pDC->SelectObject(pBrush);

     switch(m_nDrawType)

     {

     case 1:

         pDC->SetPixel(m_ptEnd,RGB(0,0,0));

         break;

     case 2:

         pDC->MoveTo(m_ptOrigin);

         pDC->LineTo(m_ptEnd);

         break;

     case 3:

         pDC->Rectangle(CRect(m_ptOrigin,m_ptEnd));

         break;

     case 4:

         pDC->Ellipse(CRect(m_ptOrigin,m_ptEnd));

         break;

     }

     pDC->SelectObject(pOldBrush);

}

Graph..h

#pragma
once

#include "atltypes.h"

 

class CGraph :
public
CObject

{

public :

     DECLARE_SERIAL( CGraph )

     CGraph(void);

     CGraph::CGraph(CPoint m_ptOrigin,CPoint m_ptEnd,UINT m_nDrawType);

     void Serialize( CArchive& ar );

     ~CGraph(void);

     CPoint m_ptOrigin;

     CPoint m_ptEnd;

     UINT m_nDrawType;

     void Draw(CDC* pDC);

};

在 View 类中添加画图功能

void CGraphicSerialView::OnDot()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=1;

}

 

void CGraphicSerialView::OnLine()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=2;

}

 

void CGraphicSerialView::OnRectangle()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=3;

}

 

void CGraphicSerialView::OnEllipse()

{

     // TODO: 在此添加命令处理程序代码

     m_nDrawType=4;

}

 

void CGraphicSerialView::OnLButtonDown(UINT nFlags, CPoint point)

{

     // TODO: 在此添加消息处理程序代码和/或调用默认值

     m_ptOrigin = point;

     CView::OnLButtonDown(nFlags, point);

}

 

void CGraphicSerialView::OnLButtonUp(UINT nFlags, CPoint point)

{

     // TODO: 在此添加消息处理程序代码和/或调用默认值

     CClientDC dc(this);

     CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));    
// 选择一个透明画刷

     dc.SelectObject(pBrush);

 

     switch(m_nDrawType)

     {

     case 1:

         dc.SetPixel(point,RGB(0,0,0));

         break;

     case 2:

         dc.MoveTo(m_ptOrigin);

         dc.LineTo(point);

         break;

     case 3:

         dc.Rectangle(m_ptOrigin.x,m_ptOrigin.y,point.x,point.y);

         break;

     case 4:

         dc.Ellipse(CRect(m_ptOrigin,point));

         break;

     default:

         break;

     }

     //
将图形信息保存起来

     CGraph *pGraph=new CGraph( m_ptOrigin , point , m_nDrawType );  
// 创建一个pGraph指针指向一个存储图形信息的“图形”

     m_obArray.Add(pGraph);//
将一群这样的pGraph
组织在一起放进m_obArray

     CView::OnLButtonUp(nFlags, point);

}

但是要保存和读取文件需要用到 Serialize

因此转到 Doc 类

void CGraphicSerialDoc::Serialize(CArchive& ar)

{

     POSITION pos=GetFirstViewPosition();//
获得第一个视类对象在对象列表中的位置

     CGraphicSerialView *pView=(CGraphicSerialView*)GetNextView(pos);//
找到当前视类对象的指针,由于这是单文档,因此只有一个视类对象

     if (ar.IsStoring())

     {

         // TODO: 在此添加存储代码

         int nCount=pView->m_obArray.GetSize();

         ar<<nCount;   //
为了在读取文件的时候能够知道元素个数,因此在保存的时候把个数也存进去

         for(int i=0;i<nCount;i++)

         {

              ar<<pView->m_obArray.GetAt(i);

         }

     }

     else

     {

         // TODO: 在此添加加载代码

         int nCount;

         ar>>nCount;

         CGraph *pGraph;

         for(int i=0;i<nCount;i++)

         {

              ar>>pGraph;

              pView->m_obArray.Add(pGraph);

         }

     }

}

但是读取数据的时候还需要重绘图像:转回View类,因为在View类加载的时候会自动调用OnDraw(),在OnDraw()中添加如下语句。

     int nCount;

     nCount=m_obArray.GetSize();

     for(int i=0;i<nCount;i++)

     {

         ((CGraph*)m_obArray.GetAt(i))->Draw(pDC);

     }

基本上完成了通过serialize保存和打开文件。

用serialize的好处:通过缓存来保存,当缓存区满了才进行一次读/写,因此提高了应用程序的效率

 

另外 , Archive 对象是支持序列化的,因此在读写数据的时候即可以按以上方法在 Doc 类的 Serialize 来保存和打开数据。

因此修改 Doc 中的 Serialize 为

void CGraphicSerialDoc::Serialize(CArchive& ar)

{

     POSITION pos=GetFirstViewPosition();//
获得第一个视类对象在对象列表中的位置

     CGraphicSerialView *pView=(CGraphicSerialView*)GetNextView(pos);//
找到当前视类对象的指针,由于这是单文档,因此只有一个视类对象

     if (ar.IsStoring())

     {

         // TODO: 在此添加存储代码

     }

     else

     {

         // TODO: 在此添加加载代码

     }

     pView->m_obArray.Serialize(ar);  //
将这个数据传递给

}

其中CObArray类对象读取数据的方法:(以下是MFC源代码,无须用户添加)

void CObArray::Serialize(CArchive& ar)

{

     ASSERT_VALID(this);

 

     CObject::Serialize(ar);

 

     if (ar.IsStoring())

     {

         ar.WriteCount(m_nSize);

         for (INT_PTR i = 0; i < m_nSize; i++)

              ar << m_pData[i];

     }

     else

     {

         DWORD_PTR nOldSize = ar.ReadCount();

         SetSize(nOldSize);

         for (INT_PTR i = 0; i < m_nSize; i++)

              ar >> m_pData[i];

     }

}

 

下面直接在 Doc 类中使用 CObArray 对象(取消之前定义在 View 类中的该类对象)

在 Doc 类中定义 CObArray m_obArray;

存图形数据的代码修改为:

     CGraphicDoc *pDoc=GetDocument();//
通过视类提供的GetDocument()方法来获得Doc类指针

     pDoc->m_obArray.Add(pGraph);

修改其他位置的 m_obArray

void CGraphicSerialView::OnDraw(CDC* pDC)

{

     ……

     nCount=pDoc->m_obArray.GetSize();

     for(int i=0;i<nCount;i++)

     {

          ((CGraph*)pDoc->m_obArray.GetAt(i))->Draw(pDC);   //Doc类中定义m_obArray的情况

     }

}

void CGraphicSerialDoc::Serialize(CArchive& ar)

{

……

     m_obArray.Serialize(ar);    
//Doc
类中定义m_obArray的情况

}

当我们新建和打开文档的时候,我们之前的文档对象并没有被销毁(系统利用 OnNewDocument 方法所新建的对象)之前在堆上建立了文档对象。此时应删除文档对象。

在文档类中重载函数 void CGraphicSerialDoc::DeleteContents()
来删除文档对象,以避免内存泄露。

void CGraphicSerialDoc::DeleteContents()

{

     int nCount;

     nCount=m_obArray.GetSize();

     /*for(int i=0;i<nCount;i++)

     {

         delete m_obArray.GetAt(i);

         //m_obArray.RemoveAt(i);

     }

     m_obArray.RemoveAll();*/

     while(nCount--)

     {

         delete m_obArray.GetAt(nCount);

         m_obArray.RemoveAt(nCount);

     }

     CDocument::DeleteContents();

}

抱歉!评论已关闭.