1、文档视图上机指导书【例 8.1】文档视图结构应用程序例子(Ex_DocView) 。(1)创建基于 CFormView 类的多文档应用程序用 MFC AppWizard(exe)创建一个默认的多文档应用程序 Ex_DocView,但在向导的第六步将 CEx_DocViewView 的基类由默认的 CView 选择为 CFormView 类,如图 8.2 所示。图 8.2 设置视图的基类为 CFormView 类(2)添加应用程序所需的数据 第一步 在 Visual C+中,选择菜单【插入】【新建类】 ,打开新建类对话框。在“Class type”组合框中选择 Generic Class,类名为
2、 CStudent,默认的类文件为Student.cpp,设置该类的父类为 CObject,并单击【OK】 按钮。如图 8.3 所示。图 8.3 添加 CStudent 类 第二步 打开项目工作区的文件视图,双击打开 Student.h 文件,如图 8.4 所示,编辑CStudent 类定义的代码如下:class CStudent : public CObject public:long m_nCode;CString m_sName;long m_lAge;double m_lScore;CStudent();CStudent(long,CString,long ,double);virtu
3、al CStudent();图 8.4 打开 Student.h 文件 第三步 同上步,编辑 Student.cpp 文件,修改类 CStudent 的构造函数实现代码如下:CStudent:CStudent(long code,CString name,long age,double score)m_nCode=code;m_sName=name; m_lAge=age;m_lScore=score; 第四步 在 Ex_DocViewDoc.h 文件中,为类 CEx_DocViewDoc 添加数据成员:class CEx_DocViewDoc : public CDocumentpublic
4、:POSITION curPos;CTypedPtrList m_dataList; 说明:m_dataList 定义为 CStudent 类指针的表 CtypedPtrList 变量,curPos 定义为POSITION 变量,用来指示当前指针。 第五步 在文件 stdafx.h 中加入添加如下代码:#include因为在程序中使用了模板类 CTypedPtrList。 第六步 在 Ex_DocViewDoc.h 文件的头部添加如下代码:#include “Student.h“ 第七步 在文档关闭时,需要清除 m_dataList 占用的内存,利用属性窗口为CEx_DocViewDoc 类加
5、入虚函数 DeleteContens(),并添加代码:void CEx_DocViewDoc:DeleteContents() while(!m_dataList.IsEmpty() delete m_dataList.RemoveHead();CDocument:DeleteContents();(3)修改 IDD_EX_DOCVIEW_FORM 对话框资源,添加应用程序所需控件: 第一步 打开项目工作区的资源视图,双击打开 IDD_EX_DOCVIEW_FORM 对话框资源如图 8.5 所示,编辑 IDD_EX_DOCVIEW_FORM 对话框资源,如图 8.6 所示。图 8.5 打开对话
6、框资源 图 8.6 编辑后的对话框控件 第二步 设置图 8.6 各控件的属性如表 8.1 所示。表 8.1 添加的控件控件 ID 标题 其他属性编辑框(学号) IDC_CODE 默认编辑框(姓名) IDC_NAME 默认编辑框(年龄) IDC_AGE 默认编辑框(成绩) IDC_SCORE 默认按钮(添加) IDC_ADD 添加 默认按钮(下一个) IDC_NEXT 下一个 默认 第三步 打开 MFC ClassWizard 的 Member Variable 页面,确定 Class Name 是CEx_DocViewView,如图 8.7 所示。选中所需的控件 ID 号,双击鼠标。依次为下列
7、控件添加成员变量,如下表 8.2 所示。图 8.7 为 CEx_DocViewView 添加成员变量表 8.2 控件变量ID 成员变量名 属性类型 变量类型IDC_CODE m_nCode Value UINTIDC_NAME m_sName Value CStringIDC_AGE m_lAge Value longIDC_SCORE m_lScore Value double(4)为按钮编写消息响应函数 第一步 用 MFC ClassWizard 为按钮 IDC_ADD 添加 BN_CLICKED 的消息映射,如图 8.8所示,单击【添加函数】按钮为 CEx_DocViewView 添加
8、OnAdd( )成员函数,再单击【编辑代码】按钮为该含函数加入下列代码:void CEx_DocViewView:OnAdd() UpdateData(); CEx_DocViewDoc *pDoc=GetDocument();ASSERT_VALID(pDoc); CStudent *pStudent;pStudent=new CStudent(m_nCode,m_sName,m_lAge,m_lScore);pDoc-m_dataList.AddTail(pStudent); pDoc-curPos=pDoc-m_dataList.GetHeadPosition();图 8.8 为按钮 I
9、DC_ADD 编写消息响应函数说明:UpdateData() 函数迫使 对话框编辑控件和相应变量之间传送数据,该函数原型为:BOOL UpdateData(BOOL bSaveAndValidate:TRUE);其中 bSaveAndValidate 为 true 时表示数据已经更新。在 CEx_DocViewView:OnAdd()函数中,取得指向文档的指针,操作文档对象的成员变量 m_dataList,首先使用用户输入的变量值构造一个新 CStudent 对象,然后将其加入m_dataList 表尾,最后将 curPos 指向表头。通过上面的操作,一个新 CStudent 对象就加到m_d
10、ataList 表中。 第二步 同上步,用 MFC ClassWizard 为按钮 IDC_NEXT 添加 BN_CLICKED 的消息映射,并增加下列代码:void CEx_DocViewView:OnNext() CEx_DocViewDoc *pDoc =GetDocument();ASSERT_VALID(pDoc);if(pDoc-curPos!=NULL) CStudent *pStudent=(CStudent *)pDoc-m_dataList.GetAt(pDoc-curPos);m_nCode=pStudent-m_nCode;m_sName=pStudent-m_sNam
11、e;m_lAge=pStudent-m_lAge;m_lScore=pStudent-m_lScore;UpdateData(false); pDoc-m_dataList.GetNext(pDoc-curPos);if(pDoc-curPos=NULL)pDoc-curPos=pDoc-m_dataList.GetHeadPosition();elseMessageBox(“当前列表中没有数据 !“);说明:CEx_DocViewView:OnNext 函数用来循环遍历 m_dataList 表,首先得到文档指针,然后判断,如果 curPos 为空,就说明没有数据,因为在 OnAdd 函数中
12、将其设在表头。如果表不空,就进行循环遍历。 第三步 编译运行并测试,结果见图 8.1【例 8.2】文档视图结构应用程序例子(Editor ) 。(1)创建单文档应用程序 Editor 第一步 用 MFC AppWizard(exe)创建一个默认的单文档应用程序 Editor,但在向导的第四步单击【Advanced 】按钮,弹出 AdvancedOption 对话框,设置文档视图结构的一些属性,如图 8.9 所示。图 8.9 Advanced Option 对话框该对话框提供的 Document Template String(文档模板字符串 )标签页,该项的值将与应用程序类中定义文档模板类对象
13、的第一个文档模板字符串 IDR_MAINFRAME 对应。它包括以下几个文本框: File extension:指定应用程序创建的文档所用的文件名后缀。输入后缀名.txt,表明 Editor 使用文本文件的后缀名.TXT; File type ID:用于在 Windows 的注册数据库中标识应用程序的文档类型; Main frame caption:主框架窗口标题,默认情况下与项目名相一致; Doc type name:文档类型名,指定与一个从 CDocument 派生的文档类相关的文档类型名; Filter name:用作“打开文件 ”、 “保存文件”对话框中的过滤器。Visual Stud
14、io 会自动根据输入的后缀名生成一个过滤器:Editor Files(*.txt ) 。这样,当在 Open File 对话框中选择 Editor Files(*.txt)时,只有以.txt 为后缀名的文件名显示在文件名列表中; File new name(short name):用于指定在 new 对话框中使用的文档名。当应用程序支持多种文档类型时,选择【Fi1e 】 【New】命令会弹出一个对话框,列出应用程序所支持的所有文档类型,供用户选择。选择一种文档类型后,自动创建相应类型的文档。 File type name(long name):用于指定当应用程序作为 OLE Automatio
15、n 服务器时使用的文档类型名,使用默认值。(2)添加应用程序所需的数据 第一步 打开项目工作区的文件视图,双击打开 EditorDoc.h 文件,如图 8.10 所示,并在该文件中,定义文档的数据成员,加入以下代码:class CEditorDoc : public CDocumentpublic:CStringList lines; / 链表 CStringList 来保存文本编辑器的数据int nLineNum; / 用于指示当前编辑行行号图 8.10 打开 EditorDoc.h 文件 第二步 同上步,打开在 EditorDoc.cpp 文件,在 CEditorDoc:OnNewDocu
16、ment()成员函数加入初始化数据成员的代码:BOOL EditorDoc:OnNewDocument()if (!CDocument:OnNewDocument() return FALSE;nLineNum=0;POSITION pos; / pos 指向链表当前元素。pos=lines.GetHeadPosition(); / 返回链表头指针while(pos!=NULL)(CString)lines.GetNext(pos).Empty();lines.RemoveAll(); / 清除链表中的所有指针return TRUE;说明:语句(CString)lines.GetNext(po
17、s).Empty()的作用是:以当前 pos 为参数,返回下一个元素指针,同时修改 pos,使它指向下一个元素。使用 强制类型转换将 GetNext()函数返回的元素指针转化为 CString 类型,然后调用 Cstring:Empty()方法清除该行中的所有字符。通过一个 while 循环,清除所有文本行的数据。一般地,类的数据成员的初始化都是在构造函数中完成的,但由于文档对象创建后,需要反复刷新而不是反复创建,因此文档类的数据成员初始化工作放在 OnNewDocument成员函数中完成而不在构造函数中做这件事情。 第三步 用 MFC ClassWizard 为 CEditorDoc 类添加
18、虚函数 DeleteContents(),如图 8.11所示。同时,增加下述代码:void CEditorDoc:DeleteContents() nLineNum=0;POSITION pos;pos=lines.GetHeadPosition();while(pos!=NULL)(CString)lines.GetNext(pos).Empty();lines.RemoveAll();CDocument:DeleteContents();图 8.11 为 CEditorDoc 类添加虚函数说明:在使用【File】【Open】命令打开一个文档或关闭应用程序时,都需要清理文档对象中的数据。文档
19、的清理是在文档的 CDocument:DeleteContents()虚函数中完成的。DeleteContents()成员函数需要反复调用,它的功能是删除文档的数据,并确信一个文档在使用前为空。有读者可能想到在析构函数中清理文档数据,但析构函数只在文档 对象结束时调用,用于清除那些在对象生存期都将存在的数据 项, 显然析构函数不能 满足重复调用的要求。(3)处理键盘输入 第一步 在文档类的头文件 EditorDoc.h 中,定义视图类的数据成员,加入以下代码:class CEditorView : public CViewint lHeight;int cWidth; 第二步 用 MFC Cl
20、assWizard 为 CEditorView 类添加虚函数 OnInitialUpdate(),并增加下列代码:void CEditorView:OnInitialUpdate() CDC *pDC=GetDC(); / 取得当前窗口的设备场境指针并存放在 pDC 中TEXTMETRIC tm;pDC-GetTextMetrics(lHeight=tm.tmHeight+tm.tmExternalLeading;cWidth=tm.tmAveCharWidth;CView:OnInitialUpdate();说明:视图类一般在 CView:OnInitialUpdate()成员函数来初始化
21、视图类的数据成员。因为这时,视图窗口已经创建,马上开始更新,那么可能影响视图显示的数据一定要在这时初始化。在以下情况下,应用程序将自动执行视图类的 OnInitialUpdate()来初始化视图类数据成员:调用 CDocument:OnNewDocument 时;调用 CDocument:OnOpenDocument 时需要清除视图原有的显示内容。TEXTMETRIC 是一个数据结构,它包含字体的宽度、高度、字的前后空白等字段。调用 CDC:GetTextMetrics()获取字体的 TEXTMETRIC,从而取得字体的宽度和高度等信息。 第三步 用 MFC ClassWizard 为类 CE
22、ditorView 添加 WM_CHAR 的消息处理函数OnChar(),如图 8.12 所示。打开 CEditorView:OnChar()函数进行编辑。修改后的 OnChar函数如下:void CEditorView:OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) CEditorDoc* pDoc=GetDocument();CClientDC dc(this);CString line(“); / 存放编辑器当前行字符串POSITION pos=NULL; / 字符串链表位置指示if(nChar=r) / 若是回车,则增加一行pDoc-nLin
23、eNum+;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(dc.TextOu
24、t(0,(int)pDoc-nLineNum*tm.tmHeight,line,line.GetLength();CView:OnChar(nChar, nRepCnt, nFlags); 图 8.12 为 CEditorView 类添加消息处理函数说明:编辑器要不断接收用户的键盘输入,就必 须处理键盘 消息。每按下一个字符,窗口就会接收到一个消息 WM_CHAR。WM_CHAR 消息是在视图类中处理的,对该消息的处理过程大致包括:读取用户输入的字符,如果 输入是一个回 车, 则将总行数 nLineNum 加 1,否则将输入字符加到当前行行末。最后调用 TextOut 函数 输出当前编辑中的文
25、本行。 第四步 修改 CEditorView 类的 OnDraw()函数,编辑后该函数的代码如下:void CEditorView:OnDraw(CDC* pDC)CEditorDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);TEXTMETRIC tm;pDC-GetTextMetrics(lHeight=tm.tmHeight+tm.tmExternalLeading;cWidth=tm.tmAveCharWidth;int y=0;POSITION pos;CString line;if(!(pos=pDoc-lines.GetHeadPosition()return;while(pos!=NULL)