第5章 文本与字体 2018/12/3 面向对象与可视化 程序设计 --Visual C++ 编程 主讲教师: 唐 龙教授 (计算机科学与技术系) 黄维通博士 (计算机与信息管理中心) 清 华 大 学 2001年2月 2018/12/3 Huang Weitong
第5章 文本与字体 2018/12/3 第十六章 文档类与视图类及其应用 2018/12/3 Huang Weitong
内容提要 概述 文档类 视图类 文档模板类 编写单文档应用程序 编写多文档应用程序 2018/12/3
第5章 文本与字体 2018/12/3 §16.1. 概述 2018/12/3 Huang Weitong
1. 单文档界面与多文档界面 VC的MFC库支持三种不同的应用程序 第5章 文本与字体 2018/12/3 1. 单文档界面与多文档界面 VC的MFC库支持三种不同的应用程序 单文档界面(SDI) 的应用程序——只有一个窗口。 多文档界面(MDI) 的应用程序——有多个子窗口,同时对多个文档进行操作。 基于对话框的应用程序 使用AppWizard创建SDI和MDI的应用程序的过程几乎完全一样。主要差别: SDI应用程序的CMainFrame类的基类为CFrameWnd,不生成CChildFrame类。 MDI应用程序的CMainFrame类的基类为CMDIFrameWnd,生成CChildFrame类。 Huang Weitong
2.视图 视图是文档的不同表现形式,每一个文档可有多个视图,但每个视图只能对应于一个确定的文档。 多文档程序需要多个视图,应解决多个文档的数据管理方法的问题。 最初的文档模板只支持主窗口; 每次打开一个新文档时都调用CDocument的函数OnNewDocument,建立一个由CMDIChildWnd派生的新的MDI子窗口; 这些窗口中保存着各种已打开的文档,所有的细节都由MFC库处理。 2018/12/3
3.文档-视图结构 文档-视图结构是基于文档的应用程序的基本框架。比传统编程方法更有利于应用程序。 数据保存在文档类的成员变量中,文档负责对数据进行管理和维护: 通过串行化过程,将数据保存到盘文件或数据库; 处理菜单、工具栏按钮和加速键的WM_COMMAND消息,但不能处理其它的Windows消息。 注意:命令消息不同于一般Windows消息和控件通知消息,可被更多种对象处理,除了窗口和视图外,还有文档、文档模板或应用程序本身。 2018/12/3
3.文档-视图结构(续) 视图在文档和用户之间起中介作用 接收用户的输入,并接受用户的修改; 直接或间接的访问文档类中的成员变量,从文档类中(不是从存储介质中)取数据,并显示之。 注意:通过调用文档和视图的接口将修改的信息反馈给文档类,实际的数据更新仍然是由文档来完成的。 2018/12/3
4.文档-视图结构的工作机制 视图通过GetDocument()获得文档的指针; 通过指针调用文档类的成员函数读取文档数据; 视图把数据显示于计算机屏幕上; 用户与视图的交互,查看/修改数据; 视图通过相关联的文档类的成员函数,将修改后的数据传递给文档对象; 文档对象获得修改过的数据之后,进行必要的修改,最后,存到磁盘文件(永久介质中) 。 2018/12/3
第5章 文本与字体 2018/12/3 §16.2. 文档及其派生类 2018/12/3 Huang Weitong
1. 文档类 所有文档类都以CDocument类为基类。 CDocument类是CCmdTarget类派生的。 在Afxwin.h中定义CDocument类。 2018/12/3
2.CDocument类的方法 CDocument类提供了文档类所需最基本功能,有两类方法: 一般方法; 虚拟方法 2018/12/3
2018/12/3
3. CDocument类的派生类构造 构造CDocument类的派生类的基本步骤: 文档的数据保存到成员变量,便于其他对象的访问。 为文档类添加成员变量。这些成员变量用来保存文档的数据; 重载Serialize成员函数,实现文档数据的串行化。 文档的数据保存到成员变量,便于其他对象的访问。 串行化由函数Serialize()实现,它有一个参数是与所打开的文件相关对象Carchive,进行文档的读或写单一操作的对象。 2018/12/3
第5章 文本与字体 2018/12/3 §16.3. 视图及其派生类 2018/12/3 Huang Weitong
1. 视图类 视图类CView是CWnd类直接派生的。 CView及其派生类都具有CWnd的所有功能,可接受任何Windows消息。 在Afxwin.h中定义CView类。 2018/12/3
2. CView类的方法 2018/12/3
3. CView类的派生类 CEditView——支持类似编辑控件所要实现的功能,常见的文本操作,多由该类实现。其直接基类不是CView类,而是类CCtrlView。 CRichEditView——支持Rich文本操作。 CTreeView——支持树型控件所实现的功能,使数据的显示方式更富于变化。 ListView——类似于CTreeView,提供了一种简捷地实现数据的不同显示的途径。 CScrollView——提供视图的滚动显示。是CView直接派生类。 注意:合理选择CView或Cview派生类可省去许多编程工作量。 2018/12/3
第5章 文本与字体 2018/12/3 §16.4. 文档模板类 2018/12/3 Huang Weitong
1.CDocTemplate类 CDocTemplate类的基类为CCmdTarget。 在Afxwin.h中定义CDocTemplate类。 2018/12/3
2. CDocTemplate类的基本函数 2018/12/3
3. CDocTemplate类的函数使用 文档类、视图类及框架窗口都由文档模板创建。 每一种文档类型都有与之相对应的一种文档模板,负责创建和管理该文档类型的所有文档。 创建过程顺序:创建文档 → 创建框架窗口 → 创建视图。 文档、视图和框架窗口三者之间是相互关联、相互协调的,彼此都包含了指向对方的指针。 2018/12/3
3. CDocTemplate类的函数使用(续) 通过文档模板的构造函数来实现文档、视图和框架窗口之间的联系,构造函数的原型如下: CDocTemplate ( UINT nIDResource, //各种资源的资源标识符 CRuntimeClass* pDocClass,//指向派生文档类的指针 CRuntimeClass* pFrameClass,//指向派生框架类指针 CRuntimeClass* pViewClass //指向CView派生类指针 ); 2018/12/3
BOOL CMymdiApp::InitInstance() { … CMultiDocTemplate* pDocTemplate2; pDocTemplate2 = new CMultiDocTemplate ( IDR_MYMDITYPE2, //MDI派生文档类的CRuntimeClass对象的指针 RUNTIME_CLASS(CMymdiDoc2), //MDI派生子框架类的CRuntimeClass对象的指针 RUNTIME_CLASS(CChildFrame), 文档模板的构造函数 2018/12/3
RUNTIME_CLASS(CMymdiView2)); //然后,使用CwinApp::AddDocTemplate()函数 //创建文档模板的对象 RUNTIME_CLASS(CMymdiView2)); //然后,使用CwinApp::AddDocTemplate()函数 //将新模板添加到应用程序的文档模板列表中。 AddDocTemplate(pDocTemplate2); … } 2018/12/3
文档模板字符串在资源文件中的定义了描绘文档模板的信息,它由最多9个子串组成。其格式如下: nIDResource <WindowTitle>\n //程序的标题栏上的标题 <DocName>\n //新文档的默认名 <FileNewName>\n //文档类型的名称 <FilterName>\n //匹配此类型文档的文件过滤器 <FilterExt>\n //文档的文件扩展名 <RegFileTypeID>\n //寄存器中的文档类型标识符 <RegFileTypeName>\n //寄存器中的文档类型名称 <FilterMacExt(FilterWinExt)>\n //允许从FilterName删除的文件扩展名 <FilterMacName(FilterWinName)> //允许Macintosh的文件类型被指定在FilterExt中 2018/12/3
第5章 文本与字体 2018/12/3 §16.5. 编写单文档应用程序 2018/12/3 Huang Weitong
[例6-1]创建一个标题为“ MySdi”的应用程序。主窗口中显示一文本“ 您好, 单文档界面的例程 2018/12/3
1. 创建工程 创建一个MFC AppWizard(exe)工程文件Mysdi,并选择Single Document的的应用程序。 2. 添加资源 程序添加如图所示的对话框。并使用ClassWizard为对话框生成CDialog类的派生类CDlgInput,并为其中的EditBox控件(其ID为IDC_EDIT1)添加相关联的成员变量m_input,其类型为Cstring。 2018/12/3
3.添加成员变量 为CMysdiDoc类添加成员变量可有两种做法 用可视化编程工具,步骤如下: 手工添加成员变量,步骤如下: 在Workspace窗口的ClassView选项卡中展开Mysdi Class,右击CMysdiDoc; 在弹出菜单中,单击Add Member Variable; 输入变量类型为CString, 变量名m_str, 访问类型public: 单击 OK。 手工添加成员变量,步骤如下: 在Workspace窗口FileView选项卡中展开Mysdi files|Header Files,双击MysdiDoc.h,打开文件; 在CMysdiDoc的定义中,找到下面的程序代码段; // Attributes public: 在此之后手工添加成员变量: CString m_str; 注:在类CMysdiDoc的定义中包括多个public块,以了区别开不同用途员。事实上,可都放到同一块中。 2018/12/3
4.文档变量初始化 在CMysdiDoc的OnNewDocument成员变量中为公有成员m_str赋以初值 “ 您好, 单文档界面的例程!”。方法是将OnNewDocument的实现替换为如下代码: BOOL CMymdiDoc2::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; m_str="您好, 单文档界面的例程!"; return TRUE; } 2018/12/3
5.视图的输出 为视图的框架窗口中显示文档类的m_str成员变量的内容,用如下代码替换类CMysdiView的OnDraw成员函数 void CMysdiView::OnDraw(CDC* pDC) { CMysdiDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CRect rectClient; GetClientRect(rectClient); // 取当前客户区的指针 CSize sizeClient=rectClient.Size(); // 取当前客户区的大小 CString str=pDoc->m_str; // 从文件中读取数据 CSize sizeTextExtent=pDC->GetTextExtent(str); // 用新字体绘制字符 pDC->TextOut((sizeClient.cx-sizeTextExtent.cx)/2, (sizeClient.cy-sizeTextExtent.cy)/2, str); } 2018/12/3
在 “ 编辑” 菜单中添加一项改变文档内容的菜单,可将所修改的文档显示文本存入磁盘文件,实现方法如下: 6.文档串行化 在 “ 编辑” 菜单中添加一项改变文档内容的菜单,可将所修改的文档显示文本存入磁盘文件,实现方法如下: (1)单击Workspace窗口 --> ResourceView选项卡 --> 展开Mysdi resources|Menu --> 双击IDR_MAINFRAME --> 为 “编辑” 菜单添加一个菜单项 “改变显示文本” --> 设置ID为ID_EDIT_CHANGETEXT。 (2)为示例程序添加如图16-7所示的对话框。然后,使用ClassWizard为对话框生成CDialog类的派生类CInputDlg,并为其中的EditBox控件(其ID为IDC_EDIT1)添加相关联的成员变量m_input,其类型为CString。 2018/12/3
6.文档串行化 (3)在类CMysdiDoc中为菜单项 “编辑|改变显示文本” (ID为ID_EDIT_CHANGETEXT)添加处理函数OnEditChangeText() void CMysdiDoc::OnEditChangetext() { CInputDlg inputDlg; //建立一个CinputDlg类的对象inputDlg //使inputDlg成为一个模式对话框 if( inputDlg.DoModal()==IDOK ) m_str = inputDlg.m_input; //获取输入的字符串 UpdateAllViews(NULL); //更新视图 } 2018/12/3
#include “InputDlg.h” //加入头文件 为使CInputDlg类在CMysdiDoc类中成为可识别的,必须在mysdiDoc.cpp文件中加入CInputDlg类的说明头文件InputDlg.h #include “InputDlg.h” //加入头文件 为将这些修改存入磁盘文件,并在需要时可打开该文件读取文档,重载CExampleDoc类的Serialize()函数来完成串行化。重载的Serialize()的代码如下: void CMysdiDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) ar<<m_str; //保存文档内容 else ar>>m_str; //读取文档内容 } 2018/12/3
第5章 文本与字体 2018/12/3 §16.6. 编写多文档应用程序 2018/12/3 Huang Weitong
[例16-2]创建一个标题为“ My MDI Test–Mymdi1” 多文档的应用程序,程序运行后,可以打开若干个文档,如图所示, Mymdi1和Mymdi21是两个文档. 2018/12/3
1. 创建MDI工程 创建一个MFC AppWizard(exe)工程文件Mymdi,并选择Multiple Document的的应用程序。 在Step4单击Advanced,并设置Advanced Option对话框。 在Step6设置基类为CEditView。 其它均取默认值设置。 2018/12/3
2. 创建第二种文档和视图类 在View-->Class Wizard-- class Info-->Add Class加入新类 在New Class对话框设置Cdocument的派生类CMymdiDoc2。 同样方法,加入CScollView的派生类CMymdiView2。 2018/12/3
3. 创建资源 (1) 文档、视图、框架窗口共同的资源ID 在Resource.h文件中,手工加入下列代码: #define IDR_MYMDITYPE2 135 这样,就定义了文档、视图、框架窗口共同的资源ID。 2018/12/3
为了使Windows能够识别.mm2的文件,在mymdi.reg文件中,加入下列代码: (2) 文档模板的资源 对于第一个文档,利用应用程序向导直接产生了一个文档模板,现在必须手工地运用这种格式加入资源。具体的方法是在mymdi.rc文件中,加入下列代码: IDR_MYMDITYPE2 “\nMymdi2\nMymdi2\nMymdi2 文件(*.mm2)\n.mm2 \nMymdi2.Document\nMymdi2 Document" 为了使Windows能够识别.mm2的文件,在mymdi.reg文件中,加入下列代码: HKEY_CLASSES_ROOT\.mm2 = Mymdi2.Document HKEY_CLASSES_ROOT\Mymdi2.Document\shell\open\command = MYMDI.EXE %1 HKEY_CLASSES_ROOT\Mymdi2.Document\shell\open\ddeexec = [open("%1")] HKEY_CLASSES_ROOT\Mymdi2.Document\shell\open\ddeexec\application = MYMDI 这样,就在Windows的注册表中进行了注册。 2018/12/3
为了实现可视化的程序,须添加一系列的资源,用户可将所修改文档显示文本保存入磁盘文件,需要时再打开并恢复该文档。 (3) 菜单、对话框资源 为了实现可视化的程序,须添加一系列的资源,用户可将所修改文档显示文本保存入磁盘文件,需要时再打开并恢复该文档。 (a) 单击Workspace窗口的ResourceView选项卡,展开Mymdi resources|Menu,加入ID为IDR_MYMDITYPE2的菜单,使它与现有的ID为IDR_MYMDITYPE的菜单完全相同,然后,在其 “编辑” 菜单添加菜单项 “改变显示文本”,并添加新菜单项ID为ID_EDIT_CHANGETEXT。 (b) 用与16-1例完全相同的方法,添加的对话框。使用ClassWizard为对话框生成CDialog类的派生类CDlgInput,并为其中的EditBox控件(其ID为IDC_EDIT1)添加相关联的成员变量m_input,其类型为CString。 2018/12/3
在应用程序的InitInstance()函数中,通过使用文档模板的构造函数创建文档模板的对象如下: 4. 代码编辑 1. 创建文档模板类 在应用程序的InitInstance()函数中,通过使用文档模板的构造函数创建文档模板的对象如下: BOOL CMymdiApp::InitInstance() { … CMultiDocTemplate* pDocTemplate2; pDocTemplate2 = new CMultiDocTemplate ( IDR_MYMDITYPE2, RUNTIME_CLASS(CMymdiDoc2), //MDI派生文档类的CRuntimeClass对象的指针 RUNTIME_CLASS(CChildFrame),//MDI派生子框架类的CRuntimeClass对象的指针 RUNTIME_CLASS(CMymdiView2) // 创建文档模板的对象 ); //使用CwinApp::AddDocTemplate() 将新模板添加到应用程序的文档模板列表中 AddDocTemplate(pDocTemplate2); … } 文档模板的构造函数 2018/12/3
第5章 文本与字体 2018/12/3 为使CMymdiDoc2类和CMymdiView2类在CMymdiApp类中成为可识别的,必须在MymdiApp.cpp文件中加入说明CMymdiDoc2类和CMymdiView2类的MymdiDoc2.h和MymdiView2.h #include "MymdiDoc2.h" //加入头文件 #include "MymdiView2.h" 2. 扩展CMymdiDoc2类 (a) 添加成员变量 用对CMysdiDoc类同样的方法,对CmymdiDoc2类添加成员变量,最终,在CMymdiDoc2.h的下述代码段中增加变量m_str声明语句。 // Attributes public: CString m_str; 2018/12/3 Huang Weitong
(b) 文档变量初始化 在CmymdiDoc2的OnNewDocument成员变量中为公有成员m_str赋以初值 “您好, 多文档界面的例程!”。方法是将OnNewDocument()代码替换如下: BOOL CMymdiDoc2::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; m_str="您好, 多文档界面的例程!"; return TRUE; } 2018/12/3
为了在CMymdiDoc类中识别CinputDlg类,必须在文件MymdiDoc2.cpp中加入说明CinputDlg类的头文件。 使用ClassWizard在类CmymdiDoc2中为菜单项 “编辑|改变显示文本” (ID为ID_EDIT_CHANGETEXT)添加处理函数OnEditChangeText,其代码如下: void CmymdiDoc2::OnEditChangetext() { CDlgInput inputDlg; //建立CinputDlg类的对象inputDlg if( inputDlg.DoModal()==IDOK )//inputDlg成为一个模式对话框 { m_str = inputDlg.m_input; //获取输入的字符串 UpdateAllViews(NULL); //更新视图 } 为了在CMymdiDoc类中识别CinputDlg类,必须在文件MymdiDoc2.cpp中加入说明CinputDlg类的头文件。 #include "InputDlg.h" //加入头文件 2018/12/3
为了存入磁盘文件中,并在需要时打开文件读取文档,应重载CExampleDoc类的Serialize()函数来完成串行化。其代码如下: 为实现Mymdi完成保存及打开文档的。先在 “编辑” 菜单中添加一条改变文档的显示文件的内容,用户可将对文档显示文本所做的修改存入磁盘文件中,需要时,再打开并恢复该文档。 为了存入磁盘文件中,并在需要时打开文件读取文档,应重载CExampleDoc类的Serialize()函数来完成串行化。其代码如下: void CMymdiDoc2::Serialize(CArchive& ar) { if (ar.IsStoring()) ar<<m_str; //保存文档内容 else ar>>m_str; //读取文档内容 } 2018/12/3
第5章 文本与字体 3.视图的输出 2018/12/3 为在视图的框架窗口中显示文档类中的m_str成员变量的内容 ,应为CmymdiView2类的OnDraw成员函数添加些代码,将OnDraw成员函数替换如下: void CmymdiView2::OnDraw(CDC* pDC) { CmymdiDoc2* pDoc = (CmymdiDoc2*)GetDocument(); ASSERT_VALID(pDoc); CRect rectClient; // 当前客户区 GetClientRect(rectClient); // 获取当前客户区的指针 CSize sizeClient=rectClient.Size(); // 获取当前客户区的大小 CString str=pDoc->m_str; // 从文件中读取数据 CSize sizeTextExtent=pDC->GetTextExtent(str);//用新字体绘制字符 pDC->TextOut((sizeClient.cxsizeTextExtent.cx)/2, (sizeClient.cysizeTextExtent.cy)/2,str); // 确定文本输出位置 } 2018/12/3 Huang Weitong
(第十六章) 完 2018/12/3
第15-16章课后练习题 编写一个具有下拉菜单和工具条单(或多)文档应用程序,菜单包括:“文件”,“绘图”等,工具条含有“直线”,“圆”,“矩形”等绘图按钮。 [注]6月4日前完成 2018/12/3
期末考核的大作业 自选应用项目(最好结合本学科内容)综合运用已学过vc++编程知识,设计实现难度适当的应用程序。 [注1]6月18日前完成(如有困难请事先与老师联系) [注2]若找不到结合本学科内容的合适应用项目,建议选择设计一个简单的二维绘图软件,实现多种方法绘制点,线, 弧,圆,矩形和正多边形等基本图形。还可加选择颜色和线型。 2018/12/3