孙鑫VC学习(第13课--文档与串行化)

来源:互联网 发布:模特兼职知乎 编辑:程序博客网 时间:2024/04/30 22:10

先建立一个MFC的单文档程序。

在框架上增加两个按钮,文件操作下面的读取文件和写入文件。并且在视类中增加消息响应函数。

下面用CArchive类对文件进行操作。使对象持久性。将CArchive类保存在持久的对象上,比如磁盘等。下次去打开程序时,从磁盘读取,在内存中重现。

void CGraphicView::OnFileWrite()

{

      // TODO: Add your command handler code here

      CFile File( "1.txt", CFile::modeCreate|CFile::modeWrite );

      CArchive ar( &File, CArchive::store );

      int i=4;

      char c='a';

      float f=1.3f;

      CString str="http://sunxin.org";

      ar<<i<<c<<f<<str;

}

则在"1.txt"中会有写入的数据;

void CGraphicView::OnFileRead()

{

      // TODO: Add your command handler code here

      //CFile File=CFile( "1.txt", CFile::modeCreate|CFile::modeWrite );

 

      //CArchive( &File, UINT nMode, int nBufSize = 4096, void* lpBuf = NULL );

      CFile File( "1.txt", CFile::modeRead );

      CArchive ar( &File, CArchive::load );

      int i/*=4*/;

      char c/*='a'*/;

      float f/*=1.3f*/;

      CString str/*="http://sunxin.org"*/;

      ar>>i>>c>>f>>str;

      CString strtemp;

      strtemp.Format("%i-%c-%f-%s",i,c,f,str);

      MessageBox(strtemp);

}

OnNewDocument(); Called by the framework as part of the File New command.在程序启动的时候被调用。

文件新建时会调用这个函数,因此可以在这个函数中修改文档的标题。

代码如下:在BOOL CGraphicDoc::OnNewDocument()中加入

SetTitle( "sunxin" );

或者在IDR_MAINFRAME字符串中修改:Graphic/n/nGraphi/n/n/nGraphic.Document/nGraphi Document

CDocTemplate::GetDocString()函数中有对这七个字符串的具体的说明。

若要程序支持另外一种类型的文档,需在BOOL CGraphicApp::InitInstance()再次构造一个文档模板,并将它加入进去。这样IDR_MAINFRAME第三个字符串的内容会出现在文件/新建对话框中。

下面增加了第45

windowTitle/ndocName/nGraphi/nWorksheets (*.xls)/n.xls/nGraphic.Document/nGraphi Document

下面是IDR_MAINFRAME7说明:

n      (1)CDocTemplate::windowTitle,主窗口标题栏上的字符串,MDI程序不需要指定,将以IDR_MAINFRAME字符串为默认值。

n      (2)CDocTemplate::docName,缺省文档的名称。如果没有指定,缺省文档的名称是无标题。

n      (3)CDocTemplate::fileNewName,文档类型的名称。如果应用程序支持多种类型的文档,此字符串将显示在"File/New"对话框中。如果没有指定,就不能够在"File/New"对话框处理这种文件。

n      (4)CDocTemplate::filterName,文档类型的描述和一个适用于此类型的通配符过滤器。这个字符串将出现在“File/Open”对话框中的文件类型列表框中。要和CDocTemplate::filterExt一起使用。

n      (5)CDocTemplate::filterExt,文档的扩展名。如果没有指定,就不能够在“File/Open”对话框中处理这种文档。要和CDocTemplate::filterName一起使用。

n      (6)CDocTemplate::regFileTypeId,如果你以::RegisterShellFileTypes向系统的注册表注册文件类型,此值会出现在HEY_CLASSES_ROOT之下成为其子项,并仅供Windows内部使用。如果没有指定,这种文件类型就无法注册。

n      (7)CDocTemplate::regFileTypeName,这也是存储在注册表中的文件类型名称。它会显示于程序中用以访问注册表的对话框内。

Document/View结构

n      MFC中,文档类负责管理数据,提供保存和加载数据的功能。视类负责数据的显示,以及给用户提供对数据的编辑和修改功能。

n      MFC给我们提供Document/View结构,将一个应用程序所需要的数据处理与显示的函数空壳都设计好了,这些函数都是虚函数,我们可以在派生类中重写这些函数。有关文件读写的操作在CDocumentSerialize函数中进行,有关数据和图形显示的操作在CViewOnDraw函数中进行。我们在其派生类中,只需要去关注SerializeOnDraw函数就可以了,其它的细节我们不需要去理会,程序就可以良好地运行。

n      当我们按下“File/Open”Application Framework会激活文件打开对话框,让你指定文件名,然后自动调用CGraphicDoc::Serialize读取文件。Application Framework还会调用CGraphicView::OnDraw,传递一个显示DC,让你重新绘制窗口内容。

n      MFC给我们提供Document/View结构,是希望我们将精力放在数据结构的设计和数据显示的操作上,而不要把时间和精力花费在对象与对象之间、模块与模块之间的通信上。

n      一个文档对象可以和多个视类对象相关联,而一个视类对象只能和一个文档对象相关联。

void CGraphicDoc::Serialize(CArchive& ar)//保存数据和加载数据

{

     if (ar.IsStoring())

     {

          // TODO: add storing code here

          int i=4;

          char c='a';

          float f=1.3f;

          CString str="http://sunxin.org";

         ar<<i<<c<<f<<str;

     }

     else

     {

          // TODO: add loading code here

          int i;

          char c;

          float f;

          CString str;

          ar>>i>>c>>f>>str;

          CString strtemp;

          strtemp.Format("%i-%c-%f-%s",i,c,f,str);

         AfxMessageBox(strtemp);

     }

}

// CArchive& ar只能一次关联一个文档对象,比如先保存,保存可以成功,但是要再次打开,就成功不了了,除非换一个文档对象。

文件新建和文件打开都是在CGraphicApp当中,它负责管理文档管理器。文档管理器有一个指针链表,管理文档模板。文档模板管理框架类,视类,文档类,三位一体来为一个文档服务。

 

CArchive类保存一个对象,则这个类必须支持串行化。

11课的Graph.cppGraph.h拷贝到本课工程目录下,加入到工程下。

//#include "Graphic.h"注释起来。

下面是一个类支持串行化的5个操作步骤:(直接写修改后的代码,红色表示改变)

class CGraph :public CObject

{

public:

     DECLARE_SERIAL( CGraph )

     void Serialize( CArchive& archive );

     CPoint m_ptOrigin;

     CPoint m_ptEnd;

     UINT m_nDrawType;

     CGraph();

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

     virtual ~CGraph();

};

 

void CGraph::Serialize( CArchive& archive )

{

   // call base class function first

   // base class is CObject in this case

   CObject::Serialize( archive );

     

   // now do the stuff for our specific class

   if( archive.IsStoring() )

       archive << m_nDrawType << m_ptOrigin<<m_ptEnd;

   else

       archive >> m_nDrawType >> m_ptOrigin>>m_ptEnd;

}

 

第三步的Defining a Constructor with No ArgumentsCGraph类中已有,此步不用。

Graph.cpp中加入IMPLEMENT_SERIAL( CPerson, CObject, 1 )

至此,CGraph类支持串行化。

 

CGraph类增加一个图形绘制的函数,将图形绘制和数据封装到一个类中,符合面向对象程序设计风格。增加的函数为void OnDraw(CDC* pDC);

void CGraph::OnDraw(CDC *pDC)

{

     CBrush *pbrush=(CBrush*)pDC->SelectStockObject(NULL_BRUSH);

   switch (m_nDrawType)

   {

   case 1:

          pDC->SetPixel(m_ptEnd,RGB(255,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;

     default:

         break;

   }

     pDC->SelectObject(pbrush);

}

增加四个菜单项:点,直线,矩形,椭圆;并且增加命令响应。(视类中)

为视类增加:

private:

     UINT m_dwstyle;

     CPoint m_dnpt;

视类构造函数中给这两个赋值。

void CGraphicView::OnDot()

{

     // TODO: Add your command handler code here

     m_dwstyle=1;

}

 

void CGraphicView::OnLine()

{

     // TODO: Add your command handler code here

     m_dwstyle=2;

}

 

void CGraphicView::OnRectangle()

{

     // TODO: Add your command handler code here

     m_dwstyle=3;

}

 

void CGraphicView::OnEllipse()

{

     // TODO: Add your command handler code here

     m_dwstyle=4;

}

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

{

     // TODO: Add your message handler code here and/or call default

     m_dnpt=point;

     CView::OnLButtonDown(nFlags, point);

}

为视类增加:

public:

     CObArray m_obarray;

#include "Graph.h"#include "Graph.h"

 

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

{

     // TODO: Add your message handler code here and/or call default

     CClientDC dc(this);

     CDC *pDC=&dc;

     CBrush *pbrush=(CBrush*)pDC->SelectStockObject(NULL_BRUSH);

   switch (m_dwstyle)

   {

   case 1:

          pDC->SetPixel(m_dnpt,RGB(255,0,0));

          break;

     case 2:

          pDC->MoveTo(m_dnpt);

          pDC->LineTo(point);

          break;

     case 3:

          pDC->Rectangle(CRect(m_dnpt,point));

          break;

     case 4:

          pDC->Ellipse(CRect(m_dnpt,point));

          break;

     default:

          break;

   }

     pDC->SelectObject(pbrush);

   CGraph *pgraph=new CGraph(m_dwstyle,m_dnpt,point);

     m_obarray.Add(pgraph);

     CView::OnLButtonUp(nFlags, point);

}

 

接下来在void CGraphicDoc::Serialize(CArchive& ar)将在集合类中保存的数据写入到文件中。

GraphicDoc.cpp中加入#include "GraphicView.h"

void CGraphicDoc::Serialize(CArchive& ar)//保存数据和加载数据

{

     POSITION pos = GetFirstViewPosition();

     CGraphicView* pView = (CGraphicView*)GetNextView(pos);

     if (ar.IsStoring())

     {

          // TODO: add storing code here

          int count=pView->m_obarray.GetSize();

          ar<<count;

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

          {

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

          }

     }

     else

     {

          int ncount;

          ar>>ncount;

          CGraph *pgraph;//自动调用不带参数的构造函数来构造这个对象,因此不需要给它分配内存空间。

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

          {

                ar>>pgraph;

                pView->m_obarray.Add(pgraph);

          }

     }

}

 

void CGraphicView::OnDraw(CDC* pDC)

{

     CGraphicDoc* pDoc = GetDocument();

     ASSERT_VALID(pDoc);

     int count;

     count=m_obarray.GetSize();

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

     {

          ((CGraph*)m_obarray.GetAt(i))->OnDraw(pDC);

     }

     // TODO: add draw code for native data here

}

 

CGraphicDoc::Serialize(CArchive& ar)实际上调用CGraph::Serialize( CArchive& archive )来完成的。由框架自动完成。

 

CObArray incorporates theIMPLEMENT_SERIAL macro to support serialization and dumping of its elements.意思是CObArray本身也支持串行化。下面来利用CObArray本身的串行化保存数据。

void CGraphicDoc::Serialize(CArchive& ar)//保存数据和加载数据

{

     POSITION pos = GetFirstViewPosition();

     CGraphicView* pView = (CGraphicView*)GetNextView(pos);

     if (ar.IsStoring())

     {

          // TODO: add storing code here

//        int count=pView->m_obarray.GetSize();

//        ar<<count;

//        for (int i=0;i<count;i++)

//        {

//             ar<<pView->m_obarray.GetAt(i);

//        }

     }

     else

     {

//        int ncount;

//        ar>>ncount;

//        CGraph *pgraph;//自动调用不带参数的构造函数来构造这个对象,因此不需要给它分配内存空间。

//        for (int i=0;i<ncount;i++)

//        {

//             ar>>pgraph;

//             pView->m_obarray.Add(pgraph);

//        }

     }

     pView->m_obarray.Serialize(ar);

 

既然文档类是存储数据的,则将CObArray m_obarray;的定义放到DOC类当中。(删除视类的)

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)中的代码修改如下:

GetDocument()->m_obarray.Add(pgraph);

void CGraphicView::OnDraw(CDC* pDC)中修改如下:

void CGraphicView::OnDraw(CDC* pDC)

{

     CGraphicDoc* pDoc = GetDocument();

     ASSERT_VALID(pDoc);

     int count;

     count=pDoc->m_obarray.GetSize();

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

     {

          ((CGraph*)pDoc->m_obarray.GetAt(i))->OnDraw(pDC);

     }

     // TODO: add draw code for native data here

}

void CGraphicDoc::Serialize(CArchive& ar)中修改如下:

m_obarray.Serialize(ar);

 

不管是新建一个文档(OnNewDocument)还是打开一个文档(OnOpenDocument),都会调用DeleteContentsensure that the document is empty and then marks the new document as clean.此工程是单文档,只有一个文档对象,它要被反复的调用,应该在此文档对象被再次使用前,删除其数据。

virtualvoid DeleteContents( );

void CGraphicDoc::DeleteContents()

{

     // TODO: Add your specialized code here and/or call the base class

     int count;

     count=m_obarray.GetSize();

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

     {

          delete m_obarray.GetAt(i);//删除指针的堆内存,删除指针值

         m_obarray.RemoveAt(i,1);

     }

     CDocument::DeleteContents();

}

这段代码有问题,因为RemoveAtRemoves one or more elements starting at a specified index in an array. In the process, it shifts down all the elements above the removed element(s). It decrements the upper bound of the array but does not free memory.

正确的代码:

void CGraphicDoc::DeleteContents()

{

     // TODO: Add your specialized code here and/or call the base class

     //int count;

     //count=m_obarray.GetSize();

     for (int i=0;i<m_obarray.GetSize();i++)

     {

          delete m_obarray.GetAt(i);//删除指针的堆内存,删除指针值

         m_obarray.RemoveAt(i,1);

     }

     CDocument::DeleteContents();

}

或者:

void CGraphicDoc::DeleteContents()

{

     // TODO: Add your specialized code here and/or call the base class

     int count;

     count=m_obarray.GetSize();

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

     {

          delete m_obarray.GetAt(i);//删除指针的堆内存,删除指针值

         //m_obarray.RemoveAt(i,1);

     }

     m_obarray.RemoveAll();//移除所有元素

     CDocument::DeleteContents();

}

或者:

void CGraphicDoc::DeleteContents()

{

     // TODO: Add your specialized code here and/or call the base class

     int count;

     count=m_obarray.GetSize();

     while (count--)

     {

          delete m_obarray.GetAt(count);

          m_obarray.RemoveAt(count,1);

     }

     CDocument::DeleteContents();

}

改变IDR_MAINFRAME中的7个字符串的值也可以在创建工程的第4步中的高级选项中设置。

原创粉丝点击