




免费预览已结束,剩余34页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章 创建和使用对话框第8章 文档和视图精讲MFC应用程序的核心是文档/视图结构。在前面章节的学习中,已经接触了不少文档/视图结构的应用程序,本章将详细分析其结构和原理,并进一步学习使用复杂的文档结构、构造更加丰富的视图。8.1 文档/视图概述使用MFC的AppWizard可以创建三种类型的应用程序:(1) 单文档界面的应用程序(SDI:Single Document Interface)(2) 多文档界面的应用程序(MDI:Multiple Documents Interface)(3) 基于对话框的应用程序(Dialog based) 基于对话框的应用程序框架非常简单,由应用程序类、对话框类构成。通过应用程序类的 InitInstance()函数,构造一个模式对话框对象;调用DoModal()函数,让Windows对话框处理程序象通常情况一样接受和分配消息;用户退出对话框后,程序也就结束了。我们已经知道SDI应用程序由应用程序类(CWinApp)、框架窗口类(CFrameWnd)、文档类(CDocument)、视图类(CView)和文档模板类(CSingleDocTemplate)共同作用。MDI应用程序与SDI应用程序的主要差别在于:MDI有CMDIFrameWnd和CMDIChildWnd两个框架窗口类,前一个派生CMainFrame类,负责菜单等界面元素的主框架窗口管理;后一个派生CChildFrame类,负责相应的文档及其视图的子框架维护。而SDI由框架窗口类CFrameWnd 派生CMainFrame类。一个文档可以有多个视图,但一个视图只能对应一个确定的文档。因此,MDI应用程序需要解决的问题是多个文档的数据管理方法。在MDI应用程序中,文档模板只支持主窗口。每打开一个新文档时,都调用文档类的成员函数OnNewDocument(),建立一个由CMDIChildWnd派生的新的MDI子窗口,在子窗口中保存已打开的文档,所有这些细节都由MFC库来处理。8.1.1 文档和视图的关系文档/视图结构的最大特点就是:把数据操作和数据表示分离开来,与数据库管理系统提供的数据库与视图的关系一致。图8-1说明了文档及其视图之间的关系。所有对数据的修改由文档对象来完成,用视图调用这个对象的方法来访问和更新数据。ABCD15565587982232337310934542265143467447818752421190203数据文档视图图8-1文档和视图的关系在MFC应用程序框架中,文档和视图的关系主要体现在:文档类和视图类对象的相互作用和相互访问上。如前面章节所述,关系图示如下:CDocument:UpdateAllViews()文档类视图类CView:GetDocument()CView:OnInitialUpdate()CView:Invalidate()CView:InvalidateRect()图8-2 文档和视图的相互访问对图示中的函数介绍如下:1 CView:GetDocument( )返回文档类的指针,通过该指针在视图类中访问并更新文档数据。2 CDocument:UpdateAllViews(CView *pSender,LPARAM lHint=0,Cobject *pHint=NULL)该函数通知与文档相连的所有或部分视图,更新窗口内容。在MFC应用程序框架中,由于文档和视图的一对多关系,当用户在一个视图中修改文档后,本视图将发生改变,相应地,与文档相连的其他视图也应与更新后的文档内容保持一致。这时,本视图便可以调用该函数向其他视图窗口发出WM_PAINT消息,通知它们更新。pSender为修改文档并发出更新通知的视图的指针,当pSender为NULL时通知与文档相连的所有视图更新,当pSender不为NULL时,通知除pSender代表的视图以外的与文档相连的所有视图更新,得到更新的视图类将调用该类的OnUpdate()函数。lHint和pHint是关于视图更新内容的提示。lHint可自定义含义,pHint是一个CObject指针,能够表示MFC所有的对象,它规定了视图需要更新的区域。经常使用来规定部分刷新区域,从而避免全部区域刷新带来的屏幕闪动。3 CView:OnUpdate( )该函数是一个虚函数,当应用程序调用CDocument:UpdateAllViews()函数时,应用程序框架会相应地调用它。还可以在应用程序视图类的派生类中,直接调用OnUpdate()函数,OnUpdate()函数访问文档,得到文档的数据,然后更新视图的数据成员或控制来反应这些变化。另外,OnUpdate()函数可以使视图的一部分无效,导致视图的OnDraw()使用文档数据来在窗口中重画。4 CView:OnInitialUpdate( )该函数也是一个虚函数,当应用程序启动时,或者用户执行菜单命令File-New或File-Open时,就会调用这个虚函数。如果要初始化视图对象,可在视图类的派生类中重载该函数,添加初始化代码。当应用程序启动时,先调用OnCreate()函数,接着就调用OnInitialUpdate()函数。在通常情况下,视图通过GetDocument()函数获得指向文档对象的指针,并通过该指针访问文档类的成员函数或数据成员获取数据。视图把数据显示于计算机屏幕上,用户通过与视图的交互来查看数据并对数据进行修改,然后视图通过相关联的文档类的成员函数将经过修改的数据传递给文档对象。文档对象获得修改过的数据之后,对其进行必要的修改,最后保存到永久介质(如磁盘文件)中。开发一个简单的文档/视图应用程序(不需要多个视图支持),只需要按下面的步骤操作:(1) 在派生的文档类添加公共类型的数据成员,这些数据成员是基本数据存储器。(2) 在派生的视图类中重载OnInitialUpdate()函数,更新视图,反映当前的文档的数据。在文档数据初始化之后或从磁盘上读入之后,应用程序框架会自动调用该函数。(3) 在派生的视图类中,使用GetDocument()来访问文档对象,使窗口处理程序、命令消息处理程序和OnDraw()函数直接读取和更新文档的数据成员。程序运行时事件发生的次序如图8-3所示,假设工程名为My。应用程序启动 构造CMyDocument对象构造CMyView对象创建View窗口调用CMyView:OnCreate(如映射)调用CMyDocument:OnNewDocument()调用CMyView:OnInitialUpdate()初始化View对象使View窗口无效调用CMyView:OnDraw()用户编辑数据 CMyView中函数更新CMyDocument的数据成员退出应用程序 删除CMyDocument对象删除CMyView对象图8-3 简单文档/视图应用程序事件发生次序8.1.2文档模板类的功能在MFC中,文档类、与文档类相关联的视图类以及为视图类提供显示的框架窗口都是由文档模板类创建的。每一种文档类型都有一种文档模板与之相对应,文档模板负责创建和管理该文档类型的所有文档。由AppWizard创建的SDI应用程序的5个基类之间的关系如图8-4所示。创建创建创建创建应用程序对象CWinApp文档模板CSingleDocTemplate文档对象CDocument框架窗口CFrameWnd视图对象CView图8-4 SDI应用程序的5个基类之间的关系模型打开SDI应用程序中应用程序类的InitInstance()成员函数,可以看到如程序清单8-1所示的一段代码,其作用是为程序定义一种文档模板类型。 程序清单8-1:动态分配SDI文档模板对象BOOL CEXSDIApp:InitInstance()。/ Register the applications document templates. Document templates/ serve as the connection between documents, frame windows and views.CSingleDocTemplate* pDocTemplate;pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CEXSDIDoc ),RUNTIME_CLASS(CMainFrame), / main SDI frame windowRUNTIME_CLASS(CEXSDIView);AddDocTemplate(pDocTemplate);。 。这段代码首先创建了CSingleDocTemplate的一个对象实例,该对象是适用于单文档程序的文档模板,它的构造函数带有四个参数:第一个参数是一个资源ID,标识了用于给框架提供加速键表、菜单和图标的资源。这个ID应用程序使用的框架窗口对它使用的每种资源类型采用同一个资源ID,通常是IDR_MAINFRAME;构造函数的后三个参数都使用了RUNTIME_CLASS宏, RUNTIME_CLASS接受一个类名作为参数,返回指向一个CRuntimeClass结构的指针,从MSDN库中了解到,该结构可以用来在运行过程中获取一个对象的类及其基类的信息,并可以动态创建类的对象实例。CRuntimeClass结构实际上是RUNTIME_CLASS的参数中指定的类的一个静态成员。 CSingleDocTemplate对象被AddDocTemplate()函数加入到应用程序对象内部的一个文档模板链表中,多数单文档程序都只支持一种文档类型,因此链表中一般只存有一个文档模板对象。写字板程序是一个能处理多种文档类型的单文档程序,在这种情况下链表中就不止一个文档模板对象了。如果没有在命令行中指定要打开的文件名,应用程序类的ProcessShellCommand()函数就会自动生成一个新文档。在生成新文档时,应用程序类将检查文档模板链表,如果链表中只有一个文档模板对象,那么就直接调用该对象的OpenDocumentFile()函数,如果链表中有多个文档模板对象,那么它会弹出一个对话框,让用户选择一种类型,然后调用所选文档模板对象的OpenDocumentFile()函数,这个函数将负责创建文档对象和主框架对象。创建创建创建创建应用程序对象CWinApp文档模板CMultiDocument文档对象CDocument框架窗口CMDIChildWnd视图对象CViewMDI应用程序的布局要比SDI应用程序的布局稍微复杂一些,图8-5展示了MDI应用程序结构的基本布局。图8-5 MDI应用程序的5个基类之间的关系模型如图中所示,MDI应用程序仍使用一个主框架来容纳菜单、工具栏和状态栏。然而,在MDI应用程序中,CMainFrame类是从MFC的CMDIFrameWnd类的,而不是从CMainFrame类派生的。CMDIFrameWnd类有着与CMainFrame类同样的可视特征,但它还实现了Windows对MDI应用程序所要求的MDI框架协议。图中示例框架窗口是CMDIChildWnd的实例,提供了Windows MDI应用程序客户区中的子窗口,用以存放视图,被包装的视图可以是任何类型,并且可以用于应用程序当前管理的任何打开的文档。同样打开一个MDI应用程序中应用程序类的InitInstance()函数,都可以找到类似程序清单8-2所示的源代码。与SDI应用程序有所区别的是MDI应用程序生成的一个CMultiDocTemplate类的文档模板对象实例,第一个参数资源ID为IDR_EXMDITYPE。程序清单8-2:动态分配MDI文档模板对象BOOL CEXMDIApp:InitInstance()。/ Register the applications document templates. Document templates/ serve as the connection between documents, frame windows and views.CMultiDocTemplate* pDocTemplate;pDocTemplate = new CMultiDocTemplate(IDR_EXMDITYPE,RUNTIME_CLASS(CEXMDIDoc),RUNTIME_CLASS(CChildFrame), / custom MDI child frameRUNTIME_CLASS(CEXMDIView);AddDocTemplate(pDocTemplate);。 。8.1.3编程实例【例8-1】本例将设计一个简单的文本编辑器(不使用CEditView类),用以说明文档/视图的原理及应用。在这个编辑器中,用户只能逐行输入字符,按回车键结束一行并换行,不支持字符的删除和插入,也没有光标指示当前编辑位置。但是用户可以选择编辑器显示文本所使用的字体。实验步骤:1 生成项目使用AppWizard生成编辑器程序的框架,项目名为Exam8_1。在MFC AppWizard-step 1选择Single Document;在MFC AppWizard 4 of 6的对话框中,单击Advanced按钮,弹出Advanced Option对话框,如图8-6所示,该对话框设置文档/视图结构和主框架窗口的一些属性。图8-6 Advanced Option 对话框该对话框有两个标签页,一页是Document Template Strings,用于设置文档/视图结构的一些属性,该项的值将与应用程序类中定义文档模板类对象的第一个参数IDR_MAINFRAME对应,它包括以下几个文本框:(1) File extersion指定应用程序创建的文档所用的文件名的后缀。输入后缀名txt。(2) File type ID用于在Windows的注册数据库中标识应用程序的文档类型。(3) Main frame caption主框架窗口标题,默认情况下与项目名相一致。(4) Doc type name该文档类型名,指定与一个从CDocument派生的文档类相关的文档类型名。(5) Filter name用作“打开文件”、“保存文件”对话框中的过滤器。Visual Studio会自动根据输入的后缀名生成一个过滤器:Exam8_1 文件(*.txt)。这样当在Open File对话框中选择Exam8_1 文件(*.txt)时,只有以txt为后缀名的文件名显示在文件名列表中。(6) File new name(short name)用于指定在new对话框中使用的文档名。当应用程序支持多种文档类型时,选择File-New菜单项会弹出一个对话框,应用程序所支持的所有文档类型供用户选择。选择一种文档类型后,自动创建相应类型的文档。(7) File type name(long name)用于指定当应用程序作为OLE Automation 服务器进使用的文档类型名,使用默认值。另一标签页是Windows Styles,用于设置主框架窗口的一些属性,包括窗口是否使用最大化按钮,窗口启动时是否最大化或最小化等。这里使用默认值。单击Close按钮,关闭Advanced Options对话框。单击Finish按钮,生成应用程序框架。2 定义文档类的数据成员对于一个文本编辑器,增加和删除一行字符是动态的,因此将使用MFC提供的集合类CStringList来保存文本行信息。CStringList中每一个元素是CString类型的对象,代表一行字符。另外还需要增加一个数据成员nLineNum用于指示当前编辑行行号。在文档类的头文件CExam8_1Doc.h中加入以下粗体部分代码:程序清单8-3:定义文档类的数据成员class CExam8_1Doc : public CDocumentprotected: / create from serialization onlyCExam8_1Doc();DECLARE_DYNCREATE(CEX8_1Doc)/ Attributespublic: CStringList lines; int nLineNum;。3 初始化文档类的数据成员由于文档对象创建后,需要反复刷新而不是反复创建,因此初始化工作放在OnNewDocument()函数中进行,而不是构造函数中进行,OnFileNew()函数会自动调用OnNewDocument()函数,代码如程序清单8-4粗体部分所示。程序清单8-4:初台化文档类的数据成员BOOL CExam8_1Doc:OnNewDocument()if (!CDocument:OnNewDocument()return FALSE;/ TODO: add reinitialization code here/ (SDI documents will reuse this document) nLineNum=0;POSITION pos;pos=lines.GetHeadPosition();while(pos!=NULL)(CString)lines.GetNext(pos).Empty(); lines.RemoveAll();return TRUE;其中:pos指向链表当前元素。CStringList的成员函数GetHeadPosition()返回链表头指针。链表的GetNext()函数以当前指针为参数,返回下一个元素指针,同时修改pos,使它指向下一个元素。使用类型转换将GetNext()函数返回的元素指针转化为CString类型,然后调用CString:Empty()方法清除该行中的所有字符。通过一个while循环,清除所有文本行的数据。CStringList的RemoveAll()函数清除链表中的所有指针。4 重载DeleteContents()函数在用File-Open菜单命令打开一个文档或关闭应用程序时,都需要清理文档对象中的数据。文档的清理是在文档类的DeleteContents()虚函数中完成的。DeleteContents()函数的功能是删除文档数据,并确信一个文档在使用前为空。通过ClassWizard重载DeleteContents()函数,然后添加代码如程序清单8-5所示。程序清单8-5:DeleteContents()函数删除文档void CExam8_1Doc:DeleteContents() / TODO: Add your specialized code here and/or call the base class nLineNum=0;POSITION pos;pos=lines.GetHeadPosition();while(pos!=NULL)(CString)lines.GetNext(pos).Empty(); lines.RemoveAll();5 编写Serialize()函数代码文档的持续化在Serialize()函数中进行。当用户执行File 菜单中的Save、Save As或Open命令时,都会自动执行这一成员函数。AppWizard给出了该函数的框架,用户需要添加代码,如程序清单8-6粗体部分所示。程序清单8-6:重载Serialize()函数void CExam8_1Doc:Serialize(CArchive& ar)CString s();int nCount=0;CString item();if (ar.IsStoring()/ 将文档对象中的数据存入文件 POSITION pos; pos=lines.GetHeadPosition();/pos指向字符串链表的头部 if(pos=NULL) return; while(pos!=NULL) item=lines.GetNext(pos);/读取一行,并使pos指向下一行 aritem;/读取文本行到item lines.AddTail(item);/将文本串加在字符串链表的尾部 nCount+;/记录行数catch(CArchiveException*e)/捕捉文件读取完毕异常e-Delete();break;nLineNum=nCount;/修改字符串总行数6 添加视图类数据成员在CExam8_1View视图类添加三个数据成员:CFont类型指针pFont,表示当前所用的字体;int型变量lHeight表示字体的高度,int型变量cWidth表示字体的宽度。添加后视图类定义如程序清单8-7粗体部分所示。程序清单8-7:添加视图类数据成员class CExam8_1View : public CViewprotected: / create from serialization onlyCExam8_1View();DECLARE_DYNCREATE(CExam8_1View)。/ ImplementationCFont *pFont;int lHeight;int cWidth;。7 视图类数据成员初始化视图类数据成员初始化一般在CView:OnInitialUpdate()函数中进行,这是由于当调用CDocument:OnNewDocument()和CDocument:OnOpenDocument()函数时,应用程序都会自动执行OnInitialUpdate()函数。本例视图类的初始化操作主要是对编辑器所使用的字体初始化。OnInitialUpdate()函数是虚函数,要通过ClassWizard重载,重载后添加初始化代码如程序清单8-8所示。程序清单8-8:初始化视图类数据成员void CExam8_1View:OnInitialUpdate() CView:OnInitialUpdate();/ TODO: Add your specialized code here and/or call the base classCDC *pDC=GetDC();pFont=new CFont();if(!(pFont-CreateFont (0,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_TT_ALWAYS,DEFAULT_QUALITY,DEFAULT_PITCH,courier New) pFont-CreateStockObject (SYSTEM_FONT); CFont* oldFont=pDC-SelectObject(pFont);TEXTMETRIC tm;pDC-GetTextMetrics(&tm);lHeight=tm.tmHeight +tm.tmExternalLeading;cWidth=tm.tmAveCharWidth ;pDC-SelectObject(oldFont); OnInitialUpdate()首先调用GetDC()函数取得当前窗口的设备场境指针并存放在pDC中。然后创建视图所用的字体,并调用SelectObject()函数将字体选入到设备场境中,同时保留原来字体到oldFont指针。最后调用GetTextMetrics()函数获取TEXTMETRIC信息,该数据结构包括字体的宽度、高度、字的前后空白等字段,初始化lHeight和cWidth变量。8 修改析构函数在关闭视图时,需要修改视图的析构函数,删除所创建的字体对象pFont,实现代码如程序清单8-9所示。程序清单8-9:删除字体对象pFontCExam8_1View:CExam8_1View()if(pFont)delete pFont;9 修改OnDraw()函数在OnDraw()函数中实现使用pFont指针所表示的字体,显示编辑器中的文本。在OnDraw()函数中需要首先访问文档对象,将字符串链表中的文本行逐行读出显示,添加的代码如程序清单8-10所示。程序清单8-10:显示编辑器中的文本的OnDraw()函数void CExam8_1View:OnDraw(CDC* pDC)CExam8_1Doc* pDoc = GetDocument();ASSERT_VALID(pDoc);/ TODO: add draw code for native data here/选择字体CFont *oldFont;oldFont=pDC-SelectObject(pFont);/计算字的高度和宽度TEXTMETRIC tm;pDC-GetTextMetrics(&tm);lHeight=tm.tmHeight +tm.tmExternalLeading;cWidth=tm.tmAveCharWidth ;int y=0;/设定纵坐标为0POSITION pos;CString line;if(!(pos=pDoc-lines.GetHeadPosition() )return ;/循环输出各文本行while(pos!=NULL)line=pDoc-lines.GetNext(pos); pDC-TextOut(0,y,line,line.GetLength();/更新y坐标,下一行文本行的位置 y+=lHeight;/恢复原来DC所用的字体pDC-SelectObject(pFont); 10 响应键盘输入编辑器要不断接收用户的键盘输入,就必须处理键盘消息。每按一下字符,窗口就会接收到一个WM_CHAR消息,该消息是在视图类中处理的,窗口接受到该消息后,把消息所包含的字符加入到当前行的末尾,并马上把输入的内容在屏幕上显示出来。首先用ClassWizard 在CExam8_1View视图类中生成WM_CHAR消息的函数OnChar(),然后打开该函数进行编辑。修改后的OnChar()函数如程序清单8-11所示。程序清单8-11:响应键盘输入消息处理函数OnChar()void CExam8_1View:OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) / TODO: Add your message handler code here and/or call defaultCExam8_1Doc* pDoc = GetDocument();CClientDC dc(this);/选择当前字体 CFont *oldFont;oldFont=dc.SelectObject(pFont);CString line();/ 存放编辑器当前字符串POSITION pos=NULL;/字符串链表位置指示if(nChar=r)/若是回车增加一行pDoc-nLineNum+; else /按行号返回字符串链表中的位置值pos=pDoc-lines.FindIndex(pDoc-nLineNum);if(!pos)/没有找到该行号对应的行,因此是一个空行,加入到字符串链表中line+=(char)nChar; pDoc-lines.AddTail(CString(line);else /当前文本还没有换行结束,将字符加入到行末line=pDoc-lines.GetAt(pos); line+=(char)nChar; pDoc-lines.SetAt(pos,line);TEXTMETRIC tm;dc.GetTextMetrics(&tm);/ 将修改后的文本行显示到屏幕 dc.TextOut(0,(int)pDoc-nLineNum*tm.tmHeight,line,line.GetLength();pDoc-SetModifiedFlag(TRUE);/ 设置文本修改标志dc.SelectObject(oldFont); 11 实现字体选择使用通用字体对话框实现字体的选择,分两步进行:(1) 在“查看”菜单中增加一个调用字体选择对话框的菜单。菜单名为Select&Font,菜单ID为ID_SELECT_FONT,提示文字为select a font for current view。(2) 使用ClassWizard为该菜单项在CExam8_1View视图类生成消息处理函数OnSelectFont(),并添加代码实现字体选择,代码见程序清单8-12所示:程序清单8-12:实现字体选择的菜单处理函数void CExam8_1View:OnSelectFont() / TODO: Add your command handler code hereCFontDialog dlg;if(dlg.DoModal()=IDOK) LOGFONT LF; dlg.GetCurrentFont(&LF);/获取当前字体信息 pFont-DeleteObject();/建立新的字体 pFont-CreateFontIndirect(&LF); this-Invalidate(); UpdateWindow();在OnSelectFont()消息处理函数中,首先定义一个选择字体通用对话框,然后创建该对话框,并返回所选的字体。字体对话框通过GetCurrentFont()函数返回字体信息。字体对象首先通过DeleteObject()删除原来的字体对象,然后通过CreateFontIndirect()函数创建新字体。调用Invalidate()函数向视图发送WM_PAINT消息,由于WM_PAINT消息级别比较低,不会立即被处理,因此调用UpdateWindow()强制更新。12 编译运行程序编译运行程序,弹出文本编辑器窗口。试着输入几行文本并存盘,建立新文件,打开存盘的文件,执行“查看”菜单下的命令Select&Font,修改文本字体。运行效果如图8-7所示。图8-7Exam8_1运行效果8.2 创建用户自定义类观察MFC库的层次结构,绝大多数类都是从CObject根类派生出来。当一个类从CObject类派生时,它继承了许多重要的特性。通过学习这些特性,可以构建用户自己的用于组织文档类数据的类。8.2.1 使用CObject类CObject类提供了3个重要的服务:1 持续性从CObject类直接或间接继承下来的类能维护一个对象持续性。即将内存中的对象数据保存到持久介质,或反过来,从持久介质中读取数据,然后重建对象。例如,CStudent类是从CObject类的派生类,那么CStudent从CObject自然地继承得到了一个担任数据持续化的虚函数Serialize(CArchive &ar),只要调用该函数,CStudent对象便可完成持续化,即完成CStudent对象的文件存取。CArchive对象ar保存了以读或写的方式打开的文件的相关信息,如文件句柄等。Serialize()函数的主要逻辑已封闭在CObject对象中,但是对什么数据进行操作,将依赖于各类对该虚函数的重载。2 动态性 前面已介绍过文档类、视图类、窗口框架类的动态创建。但从CObject类派生的类只具有一般意义上的动态性,例如,CStudent是CObject的派生类,那么CStudent自然从CObject得到一个成员函数IsKindOf(CRunTimeClass*ptr),只要调用该函数,CStudent对象就可以判断该类指针所指是不是CStudent类对象。3 诊断性CObject类提供了把对象状态转储给调试机制(如Debug输出窗口)的能力。CObject类有两种存储方式,通过CDumpContext类或者AssertValid()成员函数。Dump()成员函数能够把类的内部数据输出到CDumpContext类对象afxDump中,而afxDump是与调试输出窗口绑定的;AssertValid()成员函数能够自动维护数据的有效性。例如,在任一个SDI应用程序中,文档类都是CObject的派生类,自然继承了Dump()和AssertValid()函数,如程序清单8-13所示。程序清单8-13:CObject派生类继承的诊断性成员函数/ CEXSDIDoc diagnostics#ifdef _DEBUGvoid CEXSDIDoc:AssertValid() constCDocument:AssertValid();void CEXSDIDoc:Dump(CDumpContext& dc) constCDocument:Dump(dc);#endif /_DEBUG如果该类CEXSDIDoc具有数据成员:CString m_Name;int m_Age那么,上述的两个成员函数可以修改如程序清单8-14粗体部分所示。程序清单8-14:修改后的诊断性成员函数/ CEXSDIDoc diagnostics#ifdef _DEBUGvoid CEXSDIDoc:AssertValid() constASSERT(!m_Name.IsEmpty();/不能为空 ASSERT(!(m_Age0);/不能小于0CDocument:AssertValid();void CEXSDIDoc:Dump(CDumpContext& dc) constdcm_Name:m_Namen;dcm_Age:m_Age操作符以重建该类对象的能力,下面以CStudent类为例,在该类加入一对DECLARE_SERIAL和IMPLMENT_SERIAL宏,使CStudent具有对象持续能力。(1) 在类的定义文件Student.h中添加宏,如程序清单8-15粗体部分所示。程序清单8-15:使用DECLARE_SERIAL宏Class CStudent : public CObject 。protected : CStudent(); DECLARE_SERIAL(&CStudent)。public:/Overrides/ClassWizard generated virtual function overrides/AFX_VIRTUAL(CStudent)public:Virtual void Serialize(CArchive& ar);/AFX_VIRTUAL 。 private:int m_Grade;CString m_Name;;(2) 在类的实现文件Student.cpp中添加宏如程序清单8-16所示。程序清单8-16:使用IMPLEMENT_SERIAL宏IMPLEMENT_SERIAL(CStudent , CObject,1)void CStudent:Serialize(CArchive& ar) if (ar.IsStoring() arm_Namem_Namem_Grade;综上所述,三对宏对CObject派生类的支持程序表示为不同的层次,在类的定义中使用不同的宏,可以使类获得不同层次的动态性和持续性的支持。我们通常需要使用一个或多个从CObject类派生的类对象来组成一个文档对象内的复杂的数据,这样的派生类需要第三对类的支持,即获得对象的持续性支持。8.2.3 编程实例【例8-2】在应用程序的文档类使用一个嵌入的CStudent类对象,CStudent类保存学生记录,包括CString型的姓名和int型的成绩组成,视图类的基类使用了CFormView,CFromView视图使用编辑控件显示学生记录。运行效果如图8-9所示。图8-9 Exam8_2运行效果图实验步骤:1. 生成项目使用AppWizard生成编辑器程序的框架,项目名为Exam8_2。在MFC AppWizard-step 1选择Single Document,使用的语言选择中文(中国);在MFC AppWizard 6 of 6的对话框中选择视图类的基类为CF
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年宁波象山县人民检察院招聘真题
- 清远中考试题道法题目及答案
- 2025年辅警考试题《公安基础知识》综合能力试题库(附参考答案)
- 教师招聘之《小学教师招聘》能力提升打印大全附答案详解(培优b卷)
- 教师招聘之《幼儿教师招聘》强化训练高能及答案详解(网校专用)
- 2025年教师招聘之《幼儿教师招聘》基础试题库附答案详解【培优a卷】
- 教师招聘之《幼儿教师招聘》考前冲刺测试卷讲解附答案详解(达标题)
- 教师招聘之《小学教师招聘》通关考试题库附参考答案详解【培优b卷】
- 2025内蒙古呼伦贝尔农垦谢尔塔拉农牧场有限公司招聘45人笔试模拟及一套答案详解
- 2025呼伦贝尔莫力达瓦达斡尔族自治旗尼尔基第一中学校园引才笔试备考及1套完整答案详解
- 课件 - 1.1 无人机的基本概念与发展历程
- 2025年脂肪醇聚氧乙烯(7)醚项目市场调查研究报告
- 投放仪器合同协议书范本
- 车辆委托报废协议书
- 水利水电工程重大事故处理试题及答案
- 燃气经营企业从业人员专业培训考核大纲(试行)
- 中国共产主义青年团纪律处分条例试行解读学习
- 租油罐储存合同协议
- 《2025-0085T-QC 乘用车用差速器总成技术要求和试验方法》知识培训
- 国家能源集团陆上风电项目通 用造价指标(2024年)
- 弘扬光荣传统中密切内部关系
评论
0/150
提交评论