为WTL设计一个“文档-视图”模型.doc_第1页
为WTL设计一个“文档-视图”模型.doc_第2页
为WTL设计一个“文档-视图”模型.doc_第3页
为WTL设计一个“文档-视图”模型.doc_第4页
为WTL设计一个“文档-视图”模型.doc_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第 1 页 共 23 页 为为 WTLWTL 设计一个设计一个 文档文档 视图视图 模型模型 作者 戴霖作者 戴霖bill dai 前言 2 1 编译期虚函数调用 这样做是合法的 虽然 CMyWin 类只是被部分定义 但是类名 CMyWin 已经被列入 了 C 的递归继承列表 是可以使用的 可是为什么要这样做呢 目的何在 这里蕴含了 两个概念 一是编译期间的虚函数调用机制编译期间的虚函数调用机制 二是嵌入类嵌入类 记录并管理一个 或多个文档模板 第 3 页 共 23 页 1 编译期虚函数调用编译期虚函数调用 public void Print T ptr static cast this 这里我们称之为编译期虚函数调用 ptr SayHello class Deriver1 public Base 空类 class Deriver2 public Base protected void SayHello cout Hello Deriver2 main Deriver1 d1 Deriver2 d2 d1 Print 显示 Hello Base d2 Print 显示 Hello Deriver2 第 4 页 共 23 页 这句代码 static cast this 就是窍门所在 它在函数调用时将指向 Base 类型的 指针 this 转换为指向派生类 Deriver1 或 Deriver2 的指针 因为模板代码是在编译期间生 成的 所以只要编译器生成正确的继承列表 这种转换就是安全的 因为在这个例子里 this 只能是指向 Deriver 1 或 Deriver 2 类型的对象 不会是其他的东西 这很像 C 的多 态性 只是 Print 方法不是虚函数 要解释这是如何工作的 首先看看对每个 Print 函数的调用 在 d1 Print 语句里 对 象 Base 被指派为 Deriver1 所以代码被解释成 void Base Print Deriver1 ptr static cast this ptr SayHello 由于 Deriver1 没有重载 SayHello 所以查看基类 Base Base 有 SayHello 所以 Base 的 SayHello 被调用 再看 d2 Print 语句 这一次对象被指派为 Deriver2 类型 Print 被解释成 void Base Print Deriver2 ptr static cast this ptr SayHello 这一次 Deriver2 含有 SayHello 方法 所以 Deriver2 的 SayHello 方法被调用 这种技术的好处在于 不需要使用指向对象的指针 不需要虚函数表 节省内存 因为没有虚函数表 所以运行时不会发生调用了空指针指向的虚函数的错误 所有的函数调用在编译时确定 而非 C 虚函数机制使用的动态联编 这有利于 编译程序对代码的优化 读到这里你应该可以明白什么是编译期间的虚函数调用机制 那么嵌入类又是什么呢 其实这是 ATL 引入的一个概念 或者叫作 另一种继承机制 更合适 上例中 编译期的虚 函数调用机制能够让 Base 类安全地操纵其派生类的指针 就好像 Base 类被直接 嵌入 到 了其派生类中 而在传统的继承方法中 只能够在派生类中去操纵基类 所以 我们不妨 将嵌入类理解成 WTL ATL 的继承机制吧 明白了这样写代码的原因和工作原理之后 可以接着往下看了 2 文档类文档类 文档是用来保存数据以及关于数据的处理的 每当 SDI MDI 应用程序响应 File Open File New 命令的时候都会打开文档 一个文档可以拥有多个视图 文档 和视图的关系可以这样理解 文档是被视图观察的对象 在数据发生改变时 文档负责通 知所有与其关联的视图更新其显示 为实现该目的 文档需要维护一份包含所有关联视图 的引用列表 这通常用指针数组来实现 在 MFC 中 你的所有文档类都继承自 CDocument 基类 我们模仿这一概念 根据 WTL 继承机制将构造一个嵌入类 CDocument 它是你所有文档类的基类 我们还将构建 第 5 页 共 23 页 一个 CDocument 的基类 CDocumentBase 它是一个非模板类 而且是一个空类 为什 么呢 因为 CDocument类型中的 T 有无数种可能 除 CDocument 之外的其他类不知 道 T 是什么 如后面还要构建的视图类 它们将如何操纵文档类呢 只需建立一个 CDocumentBase 类型的指针 它可以指向任何一个文档类 是不是方便了 class CDocumentBase template class CDocument public CDocumentBase 3 视图类视图类 视图在 Windows 中就是一个窗口 模型中的视图需要依附在一个框架上才能显示为 我们看到的窗口 是用来表示文档的数据的 每个视图都对应于一个特定的文档 它拥有 一个指向其关联文档的指针以便操作数据对象 并且在数据改动后通知其它与同一文档相 关联的视图更新其显示 我们同样根据 WTL 继承机制将视图类设计成嵌入类 视图的基类 请让你的视图类 TUser View 继承它 template class CView public TDoc View TDoc View 是什么 它就是 文档 视图 模型中的 视图 你的视图类继承它以后 也 具备了与一个文档类关联的属性 TDoc View 定义为 CViewBase 为此我 们还需要一个 CViewBase 类 构建 CViewBase 类的其中一个目的与 CDocumentBase 相 同 不过它可不是一个空类 它是所有视图的抽象基类 由于一个视图是对应于一个特定 文档的 所以其模板参数必然是文档类 所有视图的抽象基类 template class CViewBase 此外 CViewBase 还将负责记录当前处于活动状态的视图 第 6 页 共 23 页 4 文档模板类文档模板类 文档模板类是整个模型的核心部分 不过它非常适合用 C 类模板来编程实现 MFC 中使用了 CRuntimeClass 类型 繁琐很多 文档模板的职责是创建一个文档及其对应的 视图 包括视图依附的子窗口窗口 它必须拥有文档 视图和框架这三个对象的信息 一 个文档模板可创建多个文档 每一个文档模板对象由文档模板管理器负责记录并管理 另 外 模仿 MFC 中的概念 文档模板类还应该持有特定文档所对应的资源 ID 菜单 工具 栏和其它资源 所以 文档模板类是一个拥有 4 个模板参数的类模板 所有文档模板的抽象基类 class CDocTemplateBase 文档模板类 template class CDocTemplate public CDocTemplateBase 5 文档模板管理器类文档模板管理器类 在 MFC 中 文档模板管理器的功能是在 CWinApp 类中提供的 而 WTL 没有该类 怎么办呢 我们决定在 MainFrame 中提供该功能 为什么呢 因为 MainFrame 位于窗口 体系的顶层 且每一个应用程序只有一个 MainFrame 当然 如果你正在做一个 SDI 应用 程序 那么文档模板管理器就是不必要的 文档模板管理器的职责是记录并管理应用程序的所有文档模板 这通常用指针数组来 实现 由于我们将在 MainFrame 中提供该功能 所以 CDocManager 文档模板管理器将被 设计成嵌入类 嵌入到 MainFrame 中去 其模板参数为 MainFrame template class CDocManager 6 上下文信息上下文信息 CCreateContext 上下文信息是一个 struct 在创建文档的过程中负责记录该文档及其所对应的文档模 板和视图的相关信息 与 MFC 中的功能一样 那就仿着 MFC 中的写吧 第 7 页 共 23 页 template struct CCreateContext TDoc m pCurrentDoc TView m pCurrentView CDocTemplateBase m pNewDocTemplate 7 改造你的代码改造你的代码 假设你已经 我们把 SDI 看成是 MDI 的特殊情况 并且你的程序只有 1 个文档模板 多文档模板只需增加类似代码即可 下面以我最近写的一个 语法高亮显示编辑器 为例 介绍代码改造过程 该编辑器由 WTL 7 5 的向导生成了一个 MDI 应用程序框架 默认视图选用了 RichEdit 最初包含下面 4 个类 CAboutDlg CChildFrame CMainFrame 和 CSHLEditorView 首先 为程序增加一个文档类 CSHLEditDoc 作如下声明 class CSHLEditorDoc public CDocument public CSHLEditorDoc void CSHLEditorDoc void protected CStringm data 必须实现的方法 CDocument 需要调用它们 public 创建新文档时调用 返回 FALSE 表示失败 BOOLOnNewDocument 保存文档时调用 返回 FALSE 表示失败 BOOLOnSaveDocument LPCTSTR lpszPathName 打开文档时调用 返回 FALSE 表示失败 BOOLOnOpenDocument LPCTSTR lpszPathName 这就是前面提过的 TDoc View typedef CViewBase CSHLEditorDocView 接着修改视图类 CSHLEditorView 改变如下 注意深灰色背景的文字 那就是你要 添加或改变的部分 typedef CWindowImpl CRichEditView 第 8 页 共 23 页 typedef CView CViewModel class CSHLEditorView public CRichEditView public CViewModel public DECLARE WND SUPERCLASS NULL RichEditCtrl GetWndClassName BOOL PreTranslateMessage MSG pMsg BEGIN MSG MAP CSHLEditorView MSG WM CREATE OnCreate CHAIN MSG MAP CViewModel END MSG MAP 必须实现的方法 virtual void OnFinalMessage HWND hWnd 对应的文档类内容发生改变时调用 如 读取了某个文件后更新显示 void OnUpdate CSHLEditorDocView pSender LPARAM Hint LPVOID pHint public HWND Create HWND hWndParent CCreateContext pContext HWND CSHLEditorView Create HWND hWndParent CCreateContext pContext 设置与该视图关联的文档 SetDocument pContext m pCurrentDoc 将该视图添加到关联文档的视图列表里 pContext m pCurrentDoc AddView this return CWindowImpl Create hWndParent NULL 0 WS CHILD WS VISIBLE WS CLIPSIBLINGS WS CLIPCHILDREN WS HSCROLL WS VSCROLL ES AUTOHSCROLL ES AUTOVSCROLL ES MULTILINE ES NOHIDESEL ES SAVESEL WS EX CLIENTEDGE UINT 0 pContext void CSHLEditorView OnFinalMessage HWND hWnd 第 9 页 共 23 页 m pDocument RemoveView this return void CSHLEditorView OnUpdate CSHLEditorDocView pSender LPARAM Hint LPVOID pHint return 视图类改造完成后 接下来是框架类 先看 CChildFrame LRESULT CChildFrame OnCreate UINT uMsg WPARAM wParam LPARAM lParam BOOL LPMDICREATESTRUCT lpms LPMDICREATESTRUCT lpcs lpCreateParams CCreateContext pContext CCreateContext lpms lParam ASSERTE pContext m pCurrentView NULL pContext m pCurrentView m hWndClient m view Create m hWnd pContext bHandled FALSE return 0 然后再把你的 MainFrame 改变如下 Class CMainFrame public CMDIFrameWindowImpl public CUpdateUI public CMessageFilter public CIdleHandler public CDocManager public CDocTemplate m DocTemplate LRESULT CMainFrame OnCreate 第 10 页 共 23 页 AddDocTemplate return 0 做好了如上改变后 下面我们开始分析一下模型的工作流程 8 模型的工作流程模型的工作流程 模型的工作起点通常位于 MainFrame 的 OnFileNew 消息响应函数中 那里有 默认的新建子窗口的过程 CChildFrame pChild new CChildFrame pChild CreateEx m hWndClient 将它们改为 创建一个新文档 GetDocTemplate 0 CreateNewDocument 该语句将调用第一个文档模板来创建一个新的文档 我们来看一下该方法 TDoc CreateNewDocument TDoc pDoc new TDoc AddDocument pDoc TFrame pFrame CreateNewFrame pDoc if pDoc OnNewDocument 如果创建新文档不成功 则销毁框架窗口 pFrame DestroyWindow RemoveDocument pDoc delete pDoc return NULL return pDoc 先创建一个新文档对象 由文档模板把它记录下来 然后创建视图 包括视图依附的 子窗口 接着调用该文档对象的 OnNewDocument 方法 去完成整个文档的创建并显示 出来 如果创建失败了则做一些收尾工作 否则返回新建的文档对象指针 创建视图时调 用了 CreateNewFrame 方法 我们也来看一下 TFrame CreateNewFrame TDoc pDoc CCreateContext context 第 11 页 共 23 页 context m pCurrentDoc pDoc context m pNewDocTemplate this TFrame GetWndClassInfo m uCommonResourceID nID TFrame pFrame new TFrame pFrame CreateEx m hWndClient NULL NULL 0 0 return pFrame 首先 我们用一个上下文结构记录当前的创建过程 保存当前创建的文档及其对应 的文档模板 接着将资源 ID 分配给将要创建的子窗口 然后该上下文结构被传递给子窗口 的创建函数 现在回头去看 CChildFrame 的 OnCreate 消息响应函数 前 3 条语句先取 出该上下文结构 再用它保存将要创建的视图 于是 该上下文结构便保存了一份完整 的 文档 视图 映射关系 接下来它又被传递给视图类 CSHLEditView 的 Create 方法 Create 方法依次设置了 视图的关联文档 和 文档的关联视图 之后再创建 CSHLEditView 视图并显示出来 至此 一个完整的 文档 视图 模型便建立了起来 9 实现代码实现代码 以下是该方案的完整实现代码 你可以将它保存到一个 C 头文件中 我给它命名为 Doc View Model for WTL h 然后在你的 stdafx h 文件中包含它 建议将它置为最后一 个包含文件 pragma once define HINT UPDATE ALL DOCUMENTS 1 define HINT DOCUMENT MODIFIED 2 超前声明 所有文档模板的基类 class CDocTemplateBase 上下文信息 template struct CCreateContext TDoc m pCurrentDoc TView m pCurrentView CDocTemplateBase m pNewDocTemplate CCreateContext memset this 0 sizeof this 第 12 页 共 23 页 用于 单文档 多视图 模型的构造函数 template CCreateContext CCreateContext pContext m pCurrentDoc pContext m pCurrentDoc m pNewDocTemplate pContext m pNewDocTemplate 所有视图的抽象基类 template class CViewBase public static CViewBase GetActiveView 返回当前活动的视图 使 用时请转换 static cast GetActiveView protected TUser Doc m pDocument 视图所对应的文档指针 static CViewBase m pActiveView 指向当前活动视图的指 针 public virtual void Update CViewBase pSender LPARAM lHint LPVOID pHint 0 lHint 与 pHint 目前没多少用处 留着以后扩展模型用吧 TUser Doc GetDocument return m pDocument void SetDocument TUser Doc pDoc m pDocument pDoc 更新关联文档模板的所有文档 void UpdateAllDocs 第 13 页 共 23 页 CDocTemplateBase pDocTemplate m pDocument GetDocTemplate int ndocs pDocTemplate GetNumDocs for int j 0 j ndocs j TUser Doc pDoc static cast pDocTemplate GetDocument j if pDoc pDoc UpdateAllViews this HINT UPDATE ALL DOCUMENTS 截获窗口激活消息 记录活动视图 你需要在自己的视图类加上 CHAIN MSG MAP CView 这里若采用 WTL 风格的消息映射 BEGIN MSG MAP EX 会导致编译错误 不 知何故 BEGIN MSG MAP CViewBase MESSAGE HANDLER WM SETFOCUS OnSetFocus END MSG MAP LRESULT OnSetFocus UINT uMsg WPARAM wParam LPARAM lParam BOOL m pActiveView this bHandled FALSE return 0 template CViewBase CViewBase m pActiveView NULL template CViewBase CViewBase GetActiveView return CViewBase m pActiveView 第 14 页 共 23 页 视图的基类 请让你的视图类继承它 template class CView public TDoc View public virtual void Update TDoc View pSender LPARAM lHint LPVOID pHint TUser View pT static cast this pT OnUpdate pSender lHint pHint HWND GetParentFrame TUser View pT static cast this HWND hWnd pT GetParent while GetWindowLong hWnd GWL EXSTYLE return hWnd typedef CView CView1 BEGIN MSG MAP CView1 CHAIN MSG MAP TDoc View END MSG MAP 所有文档的基类 class CDocumentBase 文档类 template class CDocument public CDocumentBase 第 15 页 共 23 页 public CDocument m bModified FALSE void UpdateAllViews CViewBase pSender LPARAM lHint 0 LPVOID pHint NULL int count m aViews GetSize for int i 0 i count i CViewBase pView m aViews i if pView CViewBase pSender pView Update pSender lHint pHint CViewBase AddView CViewBase pView pView SetDocument static cast this CViewBase pV static cast CViewBase pView m aViews Add pV return pView void RemoveView CViewBase pView int count m aViews GetSize for int i 0 i SetDocument NULL break 第 16 页 共 23 页 int GetNumViews const return m aViews GetSize CViewBase GetView const int pos ASSERTE pos m aViews GetSize return m aViews pos BOOL IsModified return m bModified void SetModifiedFlag BOOL bModified TRUE m bModified bModified CDocTemplateBase GetDocTemplate return m pDocTemplate 保存文档 返回 NULL 表示保存失败 请在你的文档类中实现 BOOL OnSaveDocument LPCTSTR lpszPathName BOOL SaveDocument TUser Doc pDoc static cast this if pDoc IsModified 文档已被修改 第 17 页 共 23 页 CString pathName sFilter 请直接用 LPCTSTR 类型 如果用 CString 会丢掉 0 字符 导致运 行不正常 LPCTSTR sFilter T 文本文件 txt 0 txt 0所有文件 0 0 定制保存文件对话框 如果是 Win2000 XP 则显示新样式对话框 CFileDialog dlgSaveFile FALSE T txt NULL OFN HIDEREADONLY OFN OVERWRITEPROMPT sFilter GetActiveWindow dlgSaveFile m ofn lStructSize GetVersion OnSaveDocument pathName 如果文档未能 成功保存 MessageBox NULL 保存文档失败 T 警告 MB OK MB ICONWARNING return FALSE pDoc SetModifiedFlag FALSE 保存成功 取消修 改标志 break else 用户取消了文件对话框 break for if return TRUE 第 18 页 共 23 页 protected CSimpleArray CViewBase m aViews BOOLm bModified public CDocTemplateBase m pDocTemplate 文档模板的抽象基类 class CDocTemplateBase public virtual int GetNumDocs const 0 virtual CDocumentBase Document int pos 0 返回第 pos 个文档 创建一个新文档 返回 NULL 表示创建失败 在你的文档类中实现 BOOL OnNewDocument virtual CDocumentBase CreateNewDocument 0 打开一个文档 返回 NULL 表示打开失败 请在你的文档类中实现 BOOL OnOpenDocument LPCTSTR lpszPathName virtual CDocumentBase OpenDocument BOOL bMakeVisible TRUE 0 文档模板类 template class CDocTemplate public CDocTemplateBase private CSimpleArray m aDocuments public HWND m hWndClient CDocTemplate int ndocs GetNumDocs for int i 0 i ndocs i delete m aDocuments i 第 19 页 共 23 页 m aDocuments RemoveAll TFrame CreateNewFrame TDoc pDoc CCreateContext context context m pCurrentDoc pDoc context m pNewDocTemplate this TFrame GetWndClassInfo m uCommonResourceID nID TFrame pFrame new TFrame pFrame CreateEx m hWndClient NULL NULL 0 0 return pFrame TDoc CreateNewDocument TDoc pDoc new TDoc AddDocument pDoc TFrame pFrame CreateNewFrame pDoc if pDoc OnNewDocument 如果创建新文档不成功 则销毁框架窗口 pFrame DestroyWindow RemoveDocument pDoc delete pDoc MessageBox NULL 创建新文档失败 T 警告 MB OK MB ICONWARNING return NULL return pDoc TDoc OpenDocument BOOL bMakeVisible TRUE TDoc pDoc new TDoc AddDocument pDoc 第 20 页 共 23 页 LPCTSTR sFilter T 文本文件 txt 0 txt 0所有文件 0 0 CFileDialog dlgOpenFile TRUE T txt NULL OFN OVERWRITEPROMPT sFilter GetActiveWindow dlgOpenFile m ofn lStructSize GetVersion OnOpenDocument pathName 如果文档未能 成功打开 RemoveDocument pDoc delete pDoc pDoc NULL MessageBox NULL 打开文档失败 T 警告 MB OK MB ICONWARNING else 打开文档后 创建窗口将其显示出来 TFrame pFrame CreateNewFrame pDoc pDoc UpdateAllViews NULL pDoc SetModifiedFlag FALSE break else 用户取消了文件对话框 第 21 页 共 23 页 break f

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论