使用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); } 百度:串行化可使对象被转换为某种外部的形式,比如以文件存储的形式供程序使用,或通过程序间的通讯发送到另一个处理过程。转换为外部形式的过程称为"串行化",而逆过程称为"反串行化"。
(孙鑫 十三) 文档与串行化
最新推荐文章于 2021-02-17 11:24:52 发布