`
Jack Wu
  • 浏览: 867055 次
  • 来自: ...
社区版块
存档分类
最新评论

C++--深入分析MFC文档视图结构(项目实践)

阅读更多
1 必备基础知识概述
1.1 MFC 文档视图结构程序结构总揽
当我们使用 MFC AppWizard 生成一个 MFC 程序,选用所有默认的设置(当然也是 Multiple Documents ,本文讨论主要基于 Multiple Documents ,对于 Single Document 情况仅以简单表述提及,皆因后者和前者很多相似相同之处,但前者更为复杂,并且更加常用。),假设你的程序名称为 A ,则你会得到 CMainFrame 、 CChildFrame 、 CAboutDlg 、 CADoc 、 CAView 、 CAApp 6 个类( Single Document 仅少一个 CChildFrame 类,其余均同)。这些类的具体含义将在后面给出,这里先要给出一个 MFC 支持文档视图结构程序(以下简称 App )的主要组成:
u 一个 App (对应类 CAApp )可以包含多个文档模版( CDocTemplate ),但是 MFC AppWizard (无论是 SDI 还是 MDI )都只是默认生成一个。但是在实际开发中一个文档模版不够,需要自己手工添加(在后面实际项目例子提供示例)。这个结构是通过 MFC 中 CWinApp 的成员变量 CDocManager* m_pDocManager 实现的,我们的 CAApp 正是继承自 MFC 提供的 CWinApp 类。
u CDocManager 类则拥有一个指针链表 CPtrList m_templateList 来维护这些文档模版。这些文档模版都是在 CAApp :: InitInstance ()中通过 AddDocTemplate(pDocTemplate) 。
u CDocTemplate 拥有 3 个成员变量,分别保存着 Document 、 View 、 Frame 的 CRuntimeClass 指针,另外持有成员变量 m_nIDResource ,用来指定这个 Document 显示时候采用的菜单资源。这 4 份数据都在 CAApp :: InitInstance ()中 CDocTemplate 的构造函数中指定。在 Document 中拥有一个回指 CDocTemplate 的指针( m_pDocTemplate )。
u 一个文档可以有多个视图,由 Document 中成员变量 CPtrList m_ViewList 维护。
u CFrameWnd 拥有一个成员变量 CView* m_pActiveView 指向当前活动的 View 。
u CView 中拥有一个成员变量 CDocument* m_pDocument 指向该视图相关的 Document 。
[ 注解 ] : ① MFC SDI/MDI 程序默认都默认生成了一个文档模版,并将这个文档模版 Add 到其文档模版的链表中,由于这是 MFC 默认提供的,因此这个文档模版会被插入到文档模版的第一个位置,而 MFC 也是通过这个文档模版的特定位置可以确定的。默认情况下,当我们点击 File ( Open ) / File ( New )的时候,这个文档模版会被启用。
除了侯捷先生在《深入浅出 MFC 中列出的以上的深入分析,我们还应该(很大程度上更加重要)掌握以下的关于 MFC SDI/MDI 的知识:
u 文档的本质:文档是用来保存数据以及关于数据的处理的,每当 MFC SDI/MDI 响应 File ( Open ) / File ( New )的时候都会打开一份文档。文档可以拥有多个视图。文档和视图的关系可以这样理解:文档是被视图观察的对象。
u 视图本质:视图在 Windows 中就是一个窗口,也就是一个可视化的矩形区域。视图是用来表示文档的数据的。但是每个视图必需依附于一个框架( SDI 中是 MainFrame , MDI 是 ChildFrame )。当然你可以自己去 Create 一个视图,并且去显示它。
u 框架的本质:框架实际是也是一个 Windows 窗口。但是在框架上可以放置菜单、工具栏、状态栏等。而视图则放在框架的客户区。因此 MFC 中我们看到的窗口实际上 Frame 和 View 共同作用的结果。
u 在某一时刻,程序中只有一个活动的文档、框架和视图,即当前的文档、框架、视图。
1.2 MFC SDI/MDI 各个类之间的互访
在实际项目开发中用的最多就是各个类之间的互访问,这里将网络上和书籍中提到的做了一个总结,也是笔者在实际开发中都用到过的。
访问对象
访问位置
访问实现
应用程序 App
任何位置
① AfxGetApp();
② 在要使用应用程序 App 的文件中加入:
extern CAApp theApp ,然后直接使用全局的 theApp 变量。
主框架窗口
任何位置
① AfxGetMainWnd();
② AfxGetApp()->m_pMainWnd;
视图
框架类中
GetActiveView();// 当前的活动视图
文档类中
GetFirstViewPosition (); // 可以获取全部视图
GetNextView ();
文档
文档类中
GetDocument()
文当模版类中
GetFirstDocPosition(); // 该文档模版对应全部文档
GetNextDoc();
框架类中
GetActiveDocument(); // 当前活动文当
子框架类( MDI 中)
主框架类中
① MDIGetActive ();
② GetActiveFrame ();
视图类中
GetParentFrame();
文档模版
文档类中
GetDocTemplate();
应用程序 App 中
GetFirstDocTemplatePosition();
GetNextDocTemplate();
说明: 1 )以上给出的都是方法,实际访问中可能还要进行以下简单的处理,如类型转换,循环遍历等;
2 )可能没有列举完所有可能位置的互访问,但可以通过他们的组合得到。
2 文档、视图、框架之间的关联
MFC SDI/MDI 中的核心就在于文档、视图、框架之间的关联,形成了一个有机的可运作的整体。 MFC 提供了默认的关联关系,但是在实际的项目开发中很多时候需要动态进行他们的之间的关联。
2.1 文档和视图间的关联
使用 MFC AppWizard 声称 MFC SDI/MDI 程序,在 App 类的 InitInstance ()方法中有如下代码(假设 Project 名称均为 Test ):
u SDI
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame),// main SDI frame window
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
u MDI
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_TESTTYPE,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
这里通过 CDocTemplate (无论是 SDI 中的 CSingleDocTemplate 还是 MDI 中的 CMultiDocTemplate )的构造函数,将文当、视图和框架( SDI 中与主框架, MDI 中与自框架)关联在一起了,形成了一个整体。
 
手工实现文当和视图的关联
在实际的项目开发时候仅仅依靠 MFC AppWizard 生成的文当和视图、框架是不够的,因此我们需要掌握手工进行这种关联。手工进行文当和视图的关联可以有以下两种实现方式:
l 模仿 MFC AppWizard 实现,使用 CDocTemplate 的构造函数:在上面的分
析中我们可以看到通过 CDocTemplate (无论是 SDI 中的 CSingleDocTemplate 还是 MDI 中的 CMultiDocTemplate )的构造函数我们可以获得文档、视图和框架的关联。因此可以通过模拟这种方式进行关联。具体实现方法如下:
1 创建新的文档、视图和框架类,方法是使用 VC 中的 Insert MFC Class
实现。注意到框架类选择 CMDIChildWnd 作为基类,文档类选择 CDocument 作为基类,而视图类则可以根据需要选择 CView 或其子类( CEditView )等作为基类。
2 为该框架添加菜单资源,方法是在 VC 资源窗口 Menu 菜单下添加新的菜
单,当然可以通过复制 VC 提供默认菜单进行修改。
3 在 App 类的 InitInstance ()中添加如下类似代码:
CMultiDocTemplate* m_pDocTemplate;
m_pDocTemplate = new CMultiDocTemplate(
IDR_TESTTYPE,// 改为你新建的菜单资源 ID
RUNTIME_CLASS(CTestDoc),// 改为你新建的文档类
RUNTIME_CLASS(CChildFrame), // 改为你新建的框架类
RUNTIME_CLASS(CTestView));// 改为你新建的视图类
AddDocTemplate(m_pDocTemplate);
4 为了记录这个文档模版,你可以在 App 类中添加一个 CMultiDocTemplate*
类型变量来维持这个文档模版。
l 上面给出了通过 CDocTemplate 的构造函数将文档、视图、和框架关联起来,但
是有时候我们并不想创建一个新的文档模版,我们只是想给同一个数据提供不同的结果显示,或者说是为同一个文档添加一个新的视图,并提供他们之间的一个切换。还有一种可能就是我们本来不是一个文档视图结构支持的程序,想为视图添加一个文档,更好进行业务逻辑和表示层的一个分离。第一种方法的实现方法:
Step 1 :使用 VC 6.0 新建一个 Project ,命名为: MultiView 。除选择单文档属性外,一切使用“默认”方式。于是你可以获得五个类: CMainFrame CMultiViewApp CMultiViewDoc CMultiViewView ,和 CAboutDlg
 
Step 2 :新建一个新的视图 View ,添加一个新的 MFC Class Insert >New Class ),基类为 CView (或者 CView 的派生子类,如 CEditView 等)。类的名字为 CAnotherView ,这就是新的视图;并为 CAnotherView 添加 GetDocument 的实现:
CMultiViewDoc* CAnotherView::GetDocument()
{
return (CMultiViewDoc*)m_pDocument;
}
 
Step 3 :在 CMultiViewApp 添加成员变量记录这两个视图:
private:
CView* m_pFirstView;
CView* m_pAnotherView;
给程序菜单 IDR_MAINFRAME 添加一个菜单项目“视图”,该菜单项有两个子菜单“视图一”和“视图二”,添加相应函数( void CMultiViewApp :: OnShowFirstview ()和 void CMultiViewApp :: OnShowSecondview ());
 
Step 4 :创建新的视图:在 BOOL CMultiViewApp :: InitInstance () 中添加代码:
…….
// 创建一个新的视图
CView* m_pActiveView = ((CFrameWnd*)m_pMainWnd)->GetActiveView();
m_pFirstView = m_pActiveView;
m_pAnotherView = new CAnotherView();
 
// 文档和视图关联
CDocument* m_pDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument();
 
CCreateContext context;
context.m_pCurrentDoc = m_pDoc;
 
// 创建视图
UINT m_IDFORANOTHERVIEW = AFX_IDW_PANE_FIRST + 1;
CRect rect;
m_pAnotherView->Create(NULL,NULL,WS_CHILD,rect,m_pMainWnd,
m_IDFORANOTHERVIEW,&context);
    ……
 
Step 5 :现在已经创建了视图,并且都和文档关联起来了。现在要作的就是视图间的转换。在 void CMultiViewApp :: OnShowFirstview ()中添加实现代码:
void CMultiViewApp::OnShowFirstview()
{
// TODO: Add your command handler code here
UINT temp = ::GetWindowLong(m_pAnotherView->m_hWnd, GWL_ID);
    ::SetWindowLong(m_pAnotherView->m_hWnd, GWL_ID, ::GetWindowLong(m_pFirstView->m_hWnd, GWL_ID));
    ::SetWindowLong(m_pFirstView->m_hWnd, GWL_ID, temp);
 
m_pAnotherView->ShowWindow(SW_HIDE);
m_pFirstView->ShowWindow(SW_SHOW);
 
((CFrameWnd*)m_pMainWnd)->SetActiveView(m_pFirstView); 
((CFrameWnd*) m_pMainWnd)->RecalcLayout();
    m_pFirstView->Invalidate();
}
 
void CMultiViewApp :: OnShowSecondview ()中添加实现代码:
void CMultiViewApp::OnShowSecondview()
{
// TODO: Add your command handler code here
UINT temp = ::GetWindowLong(m_pAnotherView->m_hWnd, GWL_ID);
    ::SetWindowLong(m_pAnotherView->m_hWnd, GWL_ID, ::GetWindowLong(m_pFirstView->m_hWnd, GWL_ID));
    ::SetWindowLong(m_pFirstView->m_hWnd, GWL_ID, temp);
 
m_pFirstView->ShowWindow(SW_HIDE);
m_pAnotherView->ShowWindow(SW_SHOW); 
 
((CFrameWnd*)m_pMainWnd)->SetActiveView(m_pAnotherView); 
((CFrameWnd*) m_pMainWnd)->RecalcLayout();
    m_pAnotherView->Invalidate();
}
 
Step 6 :为了演示,这里将不同的视图给予一个标记,在 CMultiViewView CAnotherView OnDraw 方法中分别添加以下代码:
pDC->TextOut(400,300,"First View");
pDC->TextOut(400,320,pDoc->GetTitle());
pDC->TextOut(400,300,"Another View");
pDC->TextOut(400,320,pDoc->GetTitle());
 
至此就大功告成了,但是实现过程中有 点说明:
1)  实现中由于使用到相关的类,因此在必要的地方要 include 相关的头文件,这里省略; CAnotherView 的默认构造函数是 Protected 的,需要将其改为 Public ,或者提供一个产生 CAnotherView 对象的方法(因要创建视图对象);
2)  这里给出的是一个示例代码,实际开发中可以通过参考实现获得自己想要实现的具体应用情况(例如视图类的不同、数量不同,更重要的还有业务逻辑的不同实现等);
第二种视图和文档关联的方法:我们使用 CCreateContext 类进行他们之间的关联,具体实现为:
m_pAnotherView = new CAnotherView(); //new 一个新的视图,可以改为你新建的视图
 
// 获取一个已有的文档,可以是你新建的文档
CDocument* m_pDoc = ((CFrameWnd*)m_pMainWnd)->GetActiveDocument();
 
// 文档和视图关联
CCreateContext context;
context.m_pCurrentDoc = m_pDoc;
 
// 创建视图
UINT m_IDFORANOTHERVIEW = AFX_IDW_PANE_FIRST + 1; // 创建视图的 ID 号,你可以自己设置
CRect rect;
m_pAnotherView->Create(NULL, NULL, WS_CHILD, rect, m_pMainWnd, m_IDFORANOTHERVIEW, &context);
l 在框架和视图关联的时候进行设置,具体见一下框架和视图关联部分。
2.2 框架和视图的关联
在第一部分分析我们知道,框架和视图其实都是 windows 窗口,不过框架提供了菜单、标题栏、状态栏等资源,而视图则只是一个矩形区域。 MFC 程序中视图决定大多数时候要依附于一个框架( SDI 中的 MainFrame 和 MDI 中的子框架窗口),可以这样理解,框架相当于一个窗口容器(当然它本身也是一个 windows 窗口),而视图则正好是放置在框架内客户区域的内容。
框架和视图的关联也可以通过 模仿 MFC AppWizard 实现,使用 CDocTemplate 的构造函数实现,即和 2.1 中文档和视图间的关联方式相同,这里就不再给出,参看上面的详细实现即可。
同上面的分析,在很多的时候我们并不是需要提供一个新的文档模版,我们只是需要显示一个新的窗口( MDI 程序),例如我们在作 MIS 系统界面管理的时候,经常出现的情况就是用户点击一个菜单选项,即弹出一个处理窗口。而要显示一个新的窗口,我们可以通过 CDocTemplate 的 OpenDocumentFile ()方法打开一个文档实现,这样就建立一套的文档、视图和框架的体系。上面已经分析到,我们看到 MFC 的窗口实际上框架和视图的一个结合体,我们并不一定要提供文档、视图、
分享到:
评论

相关推荐

    深入解析MFC

    如果做到以下几点,你就可以成为一位透彻理解MFC实现细节的专家:探索MFC文档/视图结构的内幕,从而学习视图同步、打印和打印预览;更深入地了解MFC序列化中那些没有文档记录的方面和一些没有文档记录的类,例如...

    Visual C++ 2005入门经典--源代码及课后练习答案

     本书由编程语言先驱者Ivor Horton倾力而著,是国内第一本全面、深入介绍Visual C++ 2005的经典之作! 内容简介  本书系编程语言先驱者Ivor Horton的经典之作,是学习C++编程最畅销的图书品种之一,不仅涵盖了...

    Visual C++ 2005入门经典.part08.rar (整理并添加所有书签)

    12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序和MFC 12.2 创建MFC应用程序 12.2.1 创建SDI应用程序 12.2.2 MFCApplicationwizard的输出 ...

    Visual C++ 2005入门经典.part04.rar (整理并添加所有书签)

    12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序和MFC 12.2 创建MFC应用程序 12.2.1 创建SDI应用程序 12.2.2 MFCApplicationwizard的输出 ...

    Visual C++ 2005入门经典.part05.rar (整理并添加所有书签)

    12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序和MFC 12.2 创建MFC应用程序 12.2.1 创建SDI应用程序 12.2.2 MFCApplicationwizard的输出 ...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

     李文娟,中国石油大学(华东)硕士,现供职于国家行政学院,工作后一直从事软件开发和软件项目管理工作,对计算机语言、计算机体系结构、操作系统都非常熟悉,尤其是精通C和C++编程技术. 目录 封面 -19 封底 -18 扉页...

    vc++ 开发实例源码包

    网格形式的视图,自绘了CComboBox、CEdit、CSuperGridCtrl实现。 tab 演示了CTabCtrl控件的使用方法。 tabcontrol_demo 自绘了CTabCtrl的实现。 To Create A COOL Desktop Lyrics Demo 歌词显示,效果非常好。...

    Visual C++ 2005入门经典.part07.rar (整理并添加所有书签)

    12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序和MFC 12.2 创建MFC应用程序 12.2.1 创建SDI应用程序 12.2.2 MFCApplicationwizard的输出 ...

    Visual C++ 2005入门经典.part09.rar (整理并添加所有书签)

    12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序和MFC 12.2 创建MFC应用程序 12.2.1 创建SDI应用程序 12.2.2 MFCApplicationwizard的输出 ...

    Visual C++ 2005入门经典.part06.rar (整理并添加所有书签)

    12.1 MFC的文档,视图概念 12.1.1 文档的概念 12.1.2 文档界面 12.1.3 视图的概念 12.1.4 连接文档和视图 12.1.5 应用程序和MFC 12.2 创建MFC应用程序 12.2.1 创建SDI应用程序 12.2.2 MFCApplicationwizard的输出 ...

    Visual.C.课程设计案例精编

    2.1.3 MFC的层次结构 2.2 数据交换和校验 2.2.1 什么是DDX 2.2.2 创建DDX变量 2.2.3 DoDataExchange和UpdateData函数 2.3 读文档和写文档 2.3.1 什么是序列化 2.3.2 类的序列化 2.4 消息映射 2.4.1 什么是消息...

    [Visual.C++.2010入门经典(第5版)].Ivor.Horton.part1

    注意:由于上传大小限制,此电子书分为两个压缩包,此压缩包part1为第一部分,需下载part2后同时进行解压!...第17章 创建文档和改进视图 第18章 使用对话框和控件 第19章 存储和打印文档 第20章 编写自己的dll

    visualC++2010入门经典源代码

    visualC++2010入门经典源代码 第1章 使用visual c++ 2010编程 1.1 .net framework 1 1.2 clr 2 ...第17章 创建文档和改进视图 第18章 使用对话框和控件 第19章 存储和打印文档 第20章 编写自己的dll

    vc++ 应用源码包_6

    独立打包,保证可解压,内含大量源码,网上搜集...在MFC应用程序中浏览PDF、Word文档文件 vcdialog 自绘对话框。 vc编程:自动停靠窗体,吸附窗体 OnMoving事件处理。 vc基于HTTP协议断点续传和多线程下载源代码 VC...

    吕鑫:《VC++就业培训宝典之MFC视频教程》第十三章 第三节 串行化存储原理分析

    1、演示CDocument类对比其他存储方法的优势,并深入解析串行化存储的原理。 (即CObject::Serialize回调函数的原理) 2、将CDocument类溶入视图与框架程序中,逐步将程序架构向文档模板架构模型靠近。

    vc++ 应用源码包_1

    在MFC应用程序中浏览PDF、Word文档文件 vcdialog 自绘对话框。 vc编程:自动停靠窗体,吸附窗体 OnMoving事件处理。 vc基于HTTP协议断点续传和多线程下载源代码 VC满屏开满玫瑰花 VC然输入框出现气球提示 调用...

    vc++ 应用源码包_2

    在MFC应用程序中浏览PDF、Word文档文件 vcdialog 自绘对话框。 vc编程:自动停靠窗体,吸附窗体 OnMoving事件处理。 vc基于HTTP协议断点续传和多线程下载源代码 VC满屏开满玫瑰花 VC然输入框出现气球提示 调用...

Global site tag (gtag.js) - Google Analytics