第12单元文档/视图结构 235 第12单元文档视图结构 本单元教学目标 以单文档界面(SDⅠ)应用程序为例,介绍MFC的文档视图程序结构 学习要求 理解文档/视图结构,可在 AppWizard生成的SD程序框架的基础上添加必要的代码 以生成自己的应用程序。 授课内容 大部分应用程序均要使用数据,其主要工作可以分为两部分:一是对数据的管理,如存 储、复制和查询等任务,一是对数据的处理和输入输出,包括显示和打印。MFC提供了“文 档/视图”结构支持这类应用程序 121文档/视图概念 在文档/视图结构里,文档可视为一个应用程序的数据元素的集合,MFC通过文档类提 供了大量管理和维护数据的手段。视图是数据的用户界面,可将文档的部分或全部内容在其 窗口中显示,或者通过打印机打印出来。视图还可提供用户与文档中数据的交互功能,将用 户的输入转化为对数据的操作。 在MFC中,有两种类型的文档视结构,即单文档界面(SDl: Single document Interface) 应用程序和多文档界面(MDl: Multiple Document Interface)应用程序。 在单文档界面(SD)程序中,用户在同一时刻只能操作一个文档,例如 Windows的 NotePad(记事本)程序采用的就是单文档界面。在SD程序中,打开新文档时会自动关闭 当前打开的活动文档,如果当前文档修改后尚未保存,会提示用户是否保存所做的修改。 SDI应用程序一般都提供一个File菜单,在该菜单下有一组命令,用于新建文档(New)、 打开己有文档(open)、保存或换名存盘文档等。这类程序相对比较简单,常见的应用程序 有终端仿真程序和一些工具程序。 多文档界面(MD1)应用程序允许同时对多个文档进行操作,例如 Microsoft Word和 Developer Studio本身采用的都是多文档界面。在M程序中可以打开多个文档(同时也为 每个文档打开一个窗口),可以通过切换活动窗口激活相应的文档进行编辑处理。MD1应用 程序也提供一个File菜单,用于新建、打开和保存文档。与SDl应用程序不同的是,MDI
第 12 单元 文档/视图结构 - 235 - 第 12 单元 文档/视图结构 本单元教学目标 以单文档界面(SDI)应用程序为例,介绍 MFC 的文档/视图程序结构。 学习要求 理解文档/视图结构,可在 AppWizard 生成的 SDI 程序框架的基础上添加必要的代码, 以生成自己的应用程序。 授课内容 大部分应用程序均要使用数据,其主要工作可以分为两部分:一是对数据的管理,如存 储、复制和查询等任务,一是对数据的处理和输入输出,包括显示和打印。MFC 提供了“文 档/视图”结构支持这类应用程序。 12.1 文档/视图概念 在文档/视图结构里,文档可视为一个应用程序的数据元素的集合,MFC 通过文档类提 供了大量管理和维护数据的手段。视图是数据的用户界面,可将文档的部分或全部内容在其 窗口中显示,或者通过打印机打印出来。视图还可提供用户与文档中数据的交互功能,将用 户的输入转化为对数据的操作。 在 MFC 中,有两种类型的文档视结构,即单文档界面(SDI:Single Document Interface) 应用程序和多文档界面(MDI:Multiple Document Interface)应用程序。 在单文档界面(SDI)程序中,用户在同一时刻只能操作一个文档,例如 Windows 的 NotePad(记事本)程序采用的就是单文档界面。在 SDI 程序中,打开新文档时会自动关闭 当前打开的活动文档,如果当前文档修改后尚未保存,会提示用户是否保存所做的修改。 SDI 应用程序一般都提供一个 File 菜单,在该菜单下有一组命令,用于新建文档(New)、 打开已有文档(Open)、保存或换名存盘文档等。这类程序相对比较简单,常见的应用程序 有终端仿真程序和一些工具程序。 多文档界面(MDI)应用程序允许同时对多个文档进行操作,例如 Microsoft Word 和 Developer Studio 本身采用的都是多文档界面。在 MDI 程序中可以打开多个文档(同时也为 每个文档打开一个窗口),可以通过切换活动窗口激活相应的文档进行编辑处理。MDI 应用 程序也提供一个 File 菜单,用于新建、打开和保存文档。与 SDI 应用程序不同的是,MDI
第12单元文档/视图结构 的File菜单中还有一个 Close(关闭)菜单选项,用于关闭当前打开的文档。MDI应用程序 还提供一个窗口菜单,管理所有打开的子窗口,包括对子窗口的新建、关闭、层叠、平铺等。 关闭一个窗口时,窗口内的文档也被自动关闭。在本单元中,我们只讨论SDI应用程序的 结构,MDI应用程序在第16单元中介绍。 文档/视图结构大大简化了多数应用程序的设计开发过程。其特点主要有: 1.将对数据的操作与数据显示界面分离,放在不同类的对象中处理。这种思想使得程 序模块的划分更加合理。文档对象只负责数据的管理,不涉及用户界面;视图对象只负责数 据输出和与用户的交互,可以不考虑数据的具体组织结构的细节 2.MFC在文档/视图结构中提供了许多标准的操作界面,包括新建文件、打开文件 保存文件、文档打印等,大大减轻了程序员的工作量。程序员不必再书写这些标准处理的代 码,从而可以把更多的精力放到完成应用程序特定功能的代码上。 3.支持打印、打印预览和电子邮件发送功能。程序员只需要编写很少的代码甚至根本 无需编写代码,就可以为应用程序提供“所见即所得”式的打印和打印预览这类功能。 4使用 Developer Studio的 App Wizard可生成基于文档/视结构的SD或MD框架程序, 程序员只需在其中添加与特定应用有关的部分代码,就可完成应用程序的开发工作。 然而,文档/视图结构也不是万能的。有两种情况不宜采用文档/视图结构: 1.不是面向数据的应用程序或数据量很少的应用程序,如 Windows自带的磁盘扫描程 序、时钟程序等工具软件,以及一些过程控制程序等 2.不使用标准窗口界面的程序,象一些游戏软件等 122文档视图结构程序实例 例12-1修改例9-1的吹泡泡程序。该程序的功能很简单:用户使用鼠标左键点击窗 口客户区,则可生成一圆形泡泡(其半径由一随机数确定)。这样生成的所有泡泡的位置和 大小就构成了该程序的文档,可以存放在磁盘上,也可以重新打开并修改。窗口客户区的泡 泡图象还可以通过打印机输出 说明:使用 AppWizard建立一个Win32应用程序空项目,并通过 Developer Studio 主菜单的 Project/Settings选项,在 Project Settings对话框的 General选项卡中设置使用MFC ( Using mfc in a shared DLL)。然后为项目建立一个空源程序文件并输入后面的源程序。另 外,还需通过菜单选项“ File/New..”调出New对话框为项目建立一个资源文件( Resource Script)。使用菜单选项“ File/close”将刚建立的资源文件关闭,然后使用菜单选项“ File/Open"” 调出打开文件对话框,在其中选择资源文件×××rc(×××为资源文件名),并在对话框 底部的 Open As组合框中选择Text(以文本方式打开),按下“打开(O)”按钮以文本方式 重新打开资源文件。将原来的所有内容删除,替换为: #include" afxres. h #define IDR maInframe 1 IDR MAINFRAME MENU PRELOAD DISCARDABLE BEGIN
第 12 单元 文档/视图结构 - 236 - 的 File 菜单中还有一个 Close(关闭)菜单选项,用于关闭当前打开的文档。MDI 应用程序 还提供一个窗口菜单,管理所有打开的子窗口,包括对子窗口的新建、关闭、层叠、平铺等。 关闭一个窗口时,窗口内的文档也被自动关闭。在本单元中,我们只讨论 SDI 应用程序的 结构,MDI 应用程序在第 16 单元中介绍。 文档/视图结构大大简化了多数应用程序的设计开发过程。其特点主要有: 1.将对数据的操作与数据显示界面分离,放在不同类的对象中处理。这种思想使得程 序模块的划分更加合理。文档对象只负责数据的管理,不涉及用户界面;视图对象只负责数 据输出和与用户的交互,可以不考虑数据的具体组织结构的细节。 2.MFC 在文档/视图结构中提供了许多标准的操作界面,包括新建文件、打开文件、 保存文件、文档打印等,大大减轻了程序员的工作量。程序员不必再书写这些标准处理的代 码,从而可以把更多的精力放到完成应用程序特定功能的代码上。 3.支持打印、打印预览和电子邮件发送功能。程序员只需要编写很少的代码甚至根本 无需编写代码,就可以为应用程序提供“所见即所得”式的打印和打印预览这类功能。 4.使用 Developer Studio 的 AppWizard 可生成基于文档/视结构的 SDI 或 MDI 框架程序, 程序员只需在其中添加与特定应用有关的部分代码,就可完成应用程序的开发工作。 然而,文档/视图结构也不是万能的。有两种情况不宜采用文档/视图结构: 1.不是面向数据的应用程序或数据量很少的应用程序,如 Windows 自带的磁盘扫描程 序、时钟程序等工具软件,以及一些过程控制程序等; 2.不使用标准窗口界面的程序,象一些游戏软件等。 12.2 文档/视图结构程序实例 [例 12-1] 修改例 9-1 的吹泡泡程序。该程序的功能很简单:用户使用鼠标左键点击窗 口客户区,则可生成一圆形泡泡(其半径由一随机数确定)。这样生成的所有泡泡的位置和 大小就构成了该程序的文档,可以存放在磁盘上,也可以重新打开并修改。窗口客户区的泡 泡图象还可以通过打印机输出。 说 明:使用 AppWizard 建立一个 Win32 应用程序空项目,并通过 Developer Studio 主菜单的 Project/Settings…选项,在 Project Settings 对话框的 General 选项卡中设置使用 MFC (Using MFC in a shared DLL)。然后为项目建立一个空源程序文件并输入后面的源程序。另 外,还需通过菜单选项“File/New…”调出 New 对话框为项目建立一个资源文件(Resource Script)。使用菜单选项“File/Close”将刚建立的资源文件关闭,然后使用菜单选项“File/Open” 调出打开文件对话框,在其中选择资源文件×××.rc(×××为资源文件名),并在对话框 底部的 Open As 组合框中选择 Text(以文本方式打开),按下“打开(O)”按钮以文本方式 重新打开资源文件。将原来的所有内容删除,替换为: #include "afxres.h" #define IDR_MAINFRAME 128 IDR_MAINFRAME MENU PRELOAD DISCARDABLE BEGIN
第12单元文档/视图结构 237 OPUP"文件(&F) MENUITEM"新建(&N)\ tCtrl+N ID FILE NEW MENUITEM"打开(&0)..\ tCtrl+0", ID FILE OPEN MENUITEM"保存(&S)\ tCtrl+S", ID FILE SAVE MENUITEM”另存为(&A) ID FILE SAVE AS MENUITEM SEPARATOR MENUITEM"打印(&P)..\ tCtrl+P", ID FILE_ PRINT MENUITEM“打印预览(&V)", ID FILE PRINT PREVIEW MENUITEM”打印设置(&R)...", ID FILE PRINT SETUP MENUITEM SEPARATOR MENUITEM“退出(&X)", ID APP EXIT STRINGTABLE PRELOAD DISCARDABLE BEGIN IDR MAINFRAME"吹泡泡\ n\nBub\nUb文件(*.bub)n.bub END lude"l. chs\\afxres.rc #include". chs\\afxprint rc 然后编译、连接项目 程序 / Example12-1:吹泡泡(最小文档视图框架)〃/ #include <afxwin. h> #include <afxext. h> include <afxtempl. h> /文档类 class CMy Doc public DOcumen DECLARE DYNCREATE( CMy Doc CArray< CRect, CRect&> m rect Bubble;/泡泡数组 int GetListSizeo freturn m rectBubble GetSizeo: 1 CRect Get Bubble(int index) freturn m rectBubble [index]: 1 void AddBubble( CRect rect)(m rectBubble Add (rect): H virtual BOOL On NewDocumento virtual void Delete Contents
第 12 单元 文档/视图结构 - 237 - POPUP "文件(&F)" BEGIN MENUITEM "新建(&N)\tCtrl+N", ID_FILE_NEW MENUITEM "打开(&O)...\tCtrl+O", ID_FILE_OPEN MENUITEM "保存(&S)\tCtrl+S", ID_FILE_SAVE MENUITEM "另存为(&A)...", ID_FILE_SAVE_AS MENUITEM SEPARATOR MENUITEM "打印(&P)...\tCtrl+P", ID_FILE_PRINT MENUITEM "打印预览(&V)", ID_FILE_PRINT_PREVIEW MENUITEM "打印设置(&R)...", ID_FILE_PRINT_SETUP MENUITEM SEPARATOR MENUITEM "退出(&X)", ID_APP_EXIT END END STRINGTABLE PRELOAD DISCARDABLE BEGIN IDR_MAINFRAME "吹泡泡\n\nBub\nBub 文件 (*.bub)\n.bub" END #include "l.chs\\afxres.rc" #include "l.chs\\afxprint.rc" 然后编译、连接项目。 程 序: // Example 12-1: 吹泡泡(最小文档视图框架) //////////////////// #include <afxwin.h> #include <afxext.h> #include <afxtempl.h> // 文档类 //////////////////////////////////////////////////// class CMyDoc : public CDocument { DECLARE_DYNCREATE(CMyDoc) CArray <CRect, CRect&> m_rectBubble; // 泡泡数组 public: CMyDoc(); int GetListSize(){return m_rectBubble.GetSize();} CRect GetBubble(int index){return m_rectBubble[index];} void AddBubble(CRect rect){m_rectBubble.Add(rect);} virtual BOOL OnNewDocument(); virtual void DeleteContents();
238 virtual void Serialize(CArchive& ar) IMPLEMENT DYNCREATE( CMy Doc, CDocument) /构造函数:对SDI仅调用一次,做初始化工作 CMy Doc: CMy O m rect Bubble. Setsize(256,256);//设置数组参数 /打开新文档:每次打开新文档时调用,做某些初始化工作 BOOL CMy Doc: OnNewDocument o if ( CDocument: OnNewDocument O) return False srand( unsigned)time(NUL);∥/初始化随机数发生器 return TRUE ∥/清理文档:关闭文档、建立新文档和打开文档前调用 void CMy Doc: DeleteContentso m rect Bubble. RemoveAll0;∥/泡泡数组清零 CDocument:: DeleteContents o /系列化:读写文档时自动调用 void CMyDoc: Serialize(CArchive &ar) m rectBubble Serialize(ar) /视图类/ class Cmy View: public Cview DECLARE DYNCREATE ( CMy View) ablie CMy Doca* GetDocumento freturn(CMy Doc*)m pDocument: 1 virtual void OnInitialUpdate o virtual BOOL On PreparePrinting(CPrintInfo* pInfo) virtual void OnDraw(CDC* pDC) afx msg void OnLBut tonDown UINT nFlags, CPoint point) DECLARE MESSAGE MAP O
第 12 单元 文档/视图结构 - 238 - virtual void Serialize(CArchive& ar); }; IMPLEMENT_DYNCREATE(CMyDoc, CDocument) // 构造函数:对 SDI 仅调用一次,做初始化工作 CMyDoc::CMyDoc() { m_rectBubble.SetSize(256, 256); // 设置数组参数 } // 打开新文档:每次打开新文档时调用,做某些初始化工作 BOOL CMyDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; srand((unsigned)time(NULL)); // 初始化随机数发生器 return TRUE; } // 清理文档:关闭文档、建立新文档和打开文档前调用 void CMyDoc::DeleteContents() { m_rectBubble.RemoveAll(); // 泡泡数组清零 CDocument::DeleteContents(); } // 系列化:读写文档时自动调用 void CMyDoc::Serialize(CArchive &ar) { m_rectBubble.Serialize(ar); } // 视图类 /////////////////////////////////////////////////// class CMyView : public CView { DECLARE_DYNCREATE(CMyView) public: CMyDoc* GetDocument(){return (CMyDoc*)m_pDocument;} virtual void OnInitialUpdate(); virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnDraw(CDC* pDC); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP()
第12单元文档/视图结构 239 IMPLEMENT DYNCREATE (CMyView, CView BEGIN MESSAGE MAP(CMyView, CView) ON WM LBUTTONDOWN O oN COMMAND (ID FILE PRINT, CView:: OnFilePrint) ON COMMAND(ID FILE PRINT DIRECT, CView: OnFilePrint) ON COMMAND(ID FILE PRINT PREVIEW, CView: OnFilePrintPreview END MESSAGE MAPO /更新初始化:当建立新文档或打开文档时调用 void CMyView:: OnInitialUpdateO CView:: OnInitialUpdate o Invalidate o;//更新视图 ∥/绘制视图:程序开始运行或窗体发生变化时自动调用 void CMyView: OnDraw (CDC* pDC) CMy Doc* pDoc GetDocumento /取文档指针 ASSERT VALID(pDoc pC-> SelectStockOb ject( LTGRAY BRUSH);//在视图上显示文档数据 for (int i=0: i<pDoc->GetListSizeo: i++) pDC->Ellipse(pDoc->GetBubble(i)) ′消息响应:用户点击鼠标左键时调用 void CMy View:: OnLBut ton Down( UINT n Flags, CPoint point) CMy Doc* pDoc GetDocumento) /取文档指针 ASSERT VALID(pDoc) int r= rand (%50+5: /生成泡泡半径 CRect rect Bubble(point x-r, point. y-r, point x+r, point. ytr) pDoc->AddBubble(rectBubble /修改文档数据 pDoc->SetModifiedFlago //设置修改标志 InvalidateRect( rect Bubble, FALSE);//更新视图 /准备打印:设置打印参数 BOOL CMyView:: OnPreparePrinting(CPrintInfo* pInfo) pInfo> SetMaxPage(1);∥/设置打印页数
第 12 单元 文档/视图结构 - 239 - }; IMPLEMENT_DYNCREATE(CMyView, CView) BEGIN_MESSAGE_MAP(CMyView, CView) ON_WM_LBUTTONDOWN() ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP() // 更新初始化:当建立新文档或打开文档时调用 void CMyView::OnInitialUpdate() { CView::OnInitialUpdate(); Invalidate(); // 更新视图 } // 绘制视图:程序开始运行或窗体发生变化时自动调用 void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); // 取文档指针 ASSERT_VALID(pDoc); pDC->SelectStockObject(LTGRAY_BRUSH); // 在视图上显示文档数据 for(int i=0; i<pDoc->GetListSize(); i++) pDC->Ellipse(pDoc->GetBubble(i)); } // 消息响应:用户点击鼠标左键时调用 void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { CMyDoc* pDoc = GetDocument(); // 取文档指针 ASSERT_VALID(pDoc); int r = rand()%50+5; // 生成泡泡半径 CRect rectBubble(point.x-r, point.y-r, point.x+r, point.y+r); pDoc->AddBubble(rectBubble); // 修改文档数据 pDoc->SetModifiedFlag(); // 设置修改标志 InvalidateRect(rectBubble, FALSE); // 更新视图 } // 准备打印:设置打印参数 BOOL CMyView::OnPreparePrinting(CPrintInfo* pInfo) { pInfo->SetMaxPage(1); // 设置打印页数