(孙鑫 十三) 文档与串行化

使用CArchive类对文件进行操作。MFC框架程序提供的文件新建与打开功能内部的实现机制。如何利用CDocument类的串行化存储功能保存与加载数据。如何实现类对串行化的支持,CObArray的串行化实现内幕。删出文档数据时常犯的错误。MFC框架程序的文档类和视类的关系,以及如何获得相互的指针引用。

1.增加一个文件菜单,增加两个菜单项:IDM_FILE_WRITE 写入;IDM_FILE_READ 读取,然后增加命令相应函数。
  CArchive类(文档类)
CArchive( CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf = NULL );
//;确定是加载CFile还是存储到CFile(CArchive::load/store……);缓冲区大小;缓冲区指针
   <<是存储到CArchive对象,>>是加载CArchive对象的数据
  首先在OnFileWrite中,构建一个文件对象:
	CFile file("1.txt",CFile::modeWrite | CFile::modeCreate);
	CArchive ar(&file,CArchive::store);
	int i=4;
	char ch='a';
	float f=1.3f;           //不然有警告
	CString str="http://www.baidu.com";
	ar<<i<<ch<<f<<str;

  再在OnFileRead中,读取文件
	CFile file("1.txt",CFile::modeRead);	
	CArchive ar(&file,CArchive::load);
	int i;
	char ch;
	float f;
	CString str;
	CString strResult;
	ar>>i>>ch>>f>>str;  //按写入的顺序
	strResult.Format("%d %c %f %s",i,ch,f,str);
	MessageBox(strResult);


2.在CGraphicDoc类中的OnNewDocument函数(程序启动时要调用)
virtual BOOL OnNewDocument( );

BOOL CGraphDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;

	// TODO: add reinitialization code here
	// (SDI documents will reuse this document)

	return TRUE;
}

可在这里设置文档标题:SetTitle("www.baidu.com");  //CDocument类的
在字串表中的IDR_MAINFRAME标题是:graph\n\nGraph\n\n\nGraph.Document\nGraph Document
  第一个\N\N间没有东西,所以第一次启动时标题是“无标题”,可在期间加一个“graphic”,则启动时是这个标题。

  在CGraphApp中的InitInstance函数中,构造了单文档模板:
	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CGraphDoc),
		RUNTIME_CLASS(CMainFrame),       // main SDI frame window
		RUNTIME_CLASS(CGraphView));
	AddDocTemplate(pDocTemplate);

CDocTemplate::GetDocString 
virtual BOOL GetDocString( CString& rString, enum DocStringIndex index ) const;
//index:如CDocTemplate::windowTitle/docName
  
 		String Table中IDR_MAINFRAME字符串资源中各子串的含义
(1)CDocTemplate::windowTitle,主窗口标题栏上的字符串,MDI程序不需要指定,将以IDR_MAINFRAME字符串为默认值。
(2)CDocTemplate::docName,缺省文档的名称。如果没有指定,缺省文档的名称是无标题。
(3)CDocTemplate::fileNewName,文档类型的名称。如果应用程序支持多种类型的文档,此字符串将显示在"File/New"对话框中。如果没有指定,就不能够在"File/New"对话框处理这种文件。
(4)CDocTemplate::filterName,文档类型的描述和一个适用于此类型的通配符过滤器。这个字符串将出现在“File/Open”对话框中的文件类型列表框中。要和CDocTemplate::filterExt一起使用。
(5)CDocTemplate::filterExt,文档的扩展名。如果没有指定,就不能够在“File/Open”对话框中处理这种文档。要和CDocTemplate::filterName一起使用。
(6)CDocTemplate::regFileTypeId,如果你以::RegisterShellFileTypes向系统的注册表注册文件类型,此值会出现在HEY_CLASSES_ROOT之下成为其子项,并仅供Windows内部使用。如果没有指定,这种文件类型就无法注册。
(7)CDocTemplate::regFileTypeName,这也是存储在注册表中的文件类型名称。它会显示于程序中用以访问注册表的对话框内。

(graph-windowtitle)\n\nGraph\nText Files(*.txt)\n.txt\nGraph.Document\nGraph Document
				打开时的过滤器		保存时的后缀名
  当点击新建的时候,会调用OnNewDocument函数。
  但在CWinApp中有个OnFileNew函数,然后再调用pTemplate->OpenDocumentFile(NULL),在里面CreateNewDocument,再创建Frame对象及视类对象。
  在Doc类有个void CGraphDoc::Serialize(CArchive& ar)  //连载
  在按打开、保存菜单的时候都要调用这个函数。
  在此函数中添加:
	if (ar.IsStoring())
	{
		// TODO: add storing code here
		int i=5;
		char ch='b';
		float f=1.2f;
		CString str("http://www.baidu.com");
		ar<<i<<ch<<f<<str;
	}
	else
	{
		// TODO: add loading code here
		int i=5;
		char ch='b';
		float f=1.2f;
		CString str;
		CString strResult;
		ar>>i>>ch>>f>>str;
		strResult.Format("i=%d,ch=%c,f=%f,str=%s",i,ch,f,str);
		AfxMessageBox(strResult);
	}

  CWinApp有个OnFileOpen消息响应函数,调用文档管理器中的OnFileOpen,再……
  
  给工程添加一个CGraph头文件和源文件(已写好的,画图的)。修改一些头文件,然后在头文件中,使它从CObject派生:class CGraph:public CObject  (添加CObject)
  再覆盖Serialize函数,在源文件中添加:
void CGraph::Serialize(CArchive& ar)
{
	if(ar.IsStoring())
	{
		ar<<m_nDrawType<<m_ptOrigin<<m_ptEnd;
	}
	else
	{
		ar>>m_nDrawType>>m_ptOrigin>>m_ptEnd;
	}
}

  在类声明中,在public上面申明:
	DECLARE_SERIAL(CGraph)

  再定义不带参数的构造函数

  在实现文件(即源文件),在构造函数前加个实现宏:IMPLEMENT_SERIAL(CGraph, CObject, 1 )
//1是版本号
  这样,这个源文件就支持串行化了。
  
  再在这个类中增加一个成员函数void Draw(CDC* pDC) public的。
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);
}

  这时要添加几个画图的菜单项,可在第11课的资源文件中去复制菜单资源到本程序中。然后给4个作图菜单项添加view类的命令相应,再给view类添加一些成员变量m_nDrawType/m_ptOrigin等。再添加LButtonUp和LButtonDown的消息处理函数。复制11课相关的代码即可。

  CObArray类,add的是CObject指针。
  在view类增加一个成员变量,CObArray m_obArray public的。

  在LButtonUp的switch后,添加:
	CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);  //要在view类头文件中包含
	m_obArray.Add(pGraph);				//CGraph头文件

  再在Serialize中添加:

用virtual POSITION GetFirstViewPosition( ) const;   //获取与文档相关的视图列表的第一个视图

POSITION   A value used to denote the position of an element in a collection; used by MFC collection classes. 指示在集合中的一个元素的位置

再用virtual CView* GetNextView( POSITION& rPosition ) const; //获取下一个,若没有就返NULL

添加:
	POSITION pos=GetFirstViewPosition();
	CGraphicView *pView=(CGraphicView*)GetNextView(pos);  //要包含其头文件CGraphicView的

	if (ar.IsStoring())
	{
		// TODO: add storing code here
		int nCount=pView->m_obArray.GetSize();
		ar<<nCount;  //保存对象数目
		for(int i=0;i<nCount;i++)
		{
			ar<<pView->m_obArray.GetAt(i);  //保存对象
		}
	}
	else
	{
		// TODO: add loading code here
		int nCount;
		ar>>nCount;      //输出保存的对象数目
		CGraph *pGraph;
		for(int i=0;i<nCount;i++)
		{
			ar>>pGraph;
			pView->m_obArray.Add(pGraph);
		}
	}

  再在view类中修改OnDraw重绘:
	CGraphicDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	int nCount;
	nCount=m_obArray.GetSize();
	for(int i=0;i<nCount;i++)
	{
		((CGraph*)m_obArray.GetAt(i))->Draw(pDC);
	}	


  而CObject本身也支持串行化,可将view类中if (ar.IsStoring())语句中代码注释起来,在后面添加一个:m_obArray.Serialize(ar);   //即可自动保存、打开

  既然CObject类可实现串行化,则将刚才添加的CObject注释掉,在doc类添加一个成员变量CObArray m_boArray public.则要在view中访问它。
  这时在LButtonUp后面,修改为:
	CGraphicDoc *pDoc=GetDocument();
	pDoc->m_obArray.Add(pGraph);

  在OnDraw函数中,修改:pDoc->……
  再修改doc类中的Serialize函数:	m_obArray.Serialize(ar);


		Document/View结构
在MFC中,文档类负责管理数据,提供保存和加载数据的功能。视类负责数据的显示,以及给用户提供对数据的编辑和修改功能。
MFC给我们提供Document/View结构,将一个应用程序所需要的“数据处理与显示”的函数空壳都设计好了,这些函数都是虚函数,我们可以在派生类中重写这些函数。有关文件读写的操作在CDocument的Serialize函数中进行,有关数据和图形显示的操作在CView的OnDraw函数中进行。我们在其派生类中,只需要去关注Serialize和OnDraw函数就可以了,其它的细节我们不需要去理会,程序就可以良好地运行。
当我们按下“File/Open”,Application Framework会激活文件打开对话框,让你指定文件名,然后自动调用CGraphicDoc::Serialize读取文件。Application Framework还会调用CGraphicView::OnDraw,传递一个显示DC,让你重新绘制窗口内容。
MFC给我们提供Document/View结构,是希望我们将精力放在数据结构的设计和数据显示的操作上,而不要把时间和精力花费在对象与对象之间、模块与模块之间的通信上。
一个文档对象可以和多个视类对象相关联,而一个视类对象只能和一个文档对象相关联。


  在新建的时候,调用OnNewDocument,要先将文档对象清空,用CDocumnet的DeleteContents删除内容,将之清空。
  在文档类添加虚函数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语句:
	while(nCount--)
	{
		delete m_obArray.GetAt(nCount);
		m_obArray.RemoveAt(nCount);
	}

百度:串行化可使对象被转换为某种外部的形式,比如以文件存储的形式供程序使用,或通过程序间的通讯发送到另一个处理过程。转换为外部形式的过程称为"串行化",而逆过程称为"反串行化"。 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值