当前位置: 首页 > news >正文

[转载]MFC一个文档不同视图

  MDI(MultipleDocument Interface)是Windows界面的一种规范,它建立多个窗口来浏览文档数据,如Windows中的ProgramManager等都是按MDI规范实现的。在实际工程软件开发中,许多程序员将其作为一种实现多窗口的标准方法。微软基础类库(MicrosoftFoundationClassLibrary,简称MFC库),是微软公司为方便Windows程序开发所提供的一个功能强大的通用类库。MFC的核心是以类的形式封装了大量WindowsAPI。在可视化编程语言VC++下应用MFC是目前开发Windows程序最方便的途径之一。VC++提供的各种开发工具如AppWizard、ClassWizard和AppStudio,可以建立起具备基本功能的Windows框架程序(Framework)。而程序员所需要做的工作就是将自己特有的代码填入到框架程序中去,从而极大地减少了用户界面编程的工作量,加快了开发速度。关于MDI的标准开发方法可参考一般的Windows编程书籍,本文将介绍利用MFC实现MDI界面。

  MFC2.0以上版本支持“文档/浏览视窗”(Document/View)结构模式。由文档负责管理数据,浏览视窗负责数据显示及与用户的交互,从而实现了数据与界面的分离,使整个程序设计更具规范化、模块化。MFC中,“文档”由类CDocument及其派生类实现(简称Doc类);“浏览视窗”由类CView及其派生类实现(简称View类)。二者都包含于应用程序的框架窗口中,并由其管理。使用单文档时,框架窗口由类CFrameWnd及其派生类实现;使用多文档时,框架窗口是利用类CMDIFrameWnd和CMDIChildWnd实现。由文档模板将文档、浏览窗口和框架窗口三者联系起来。

  当程序员在AppWizard的Option选项中选择MultipleDocumentInterface时,MFC构架程序(Framework)将自动生成实现MDI基本功能的代码。类CMDIFrameWnd负责整个应用程序的主框架窗口;类CMDIChildWnd实现MDI的子窗口框架,它不带菜单项,而与主框架窗口共享菜单。主框架窗口依据当前激活的子窗口自动更换菜单项。CView则负责MDI子窗口客户区中显示的具体内容。例如,AppWizard的以M01为Project名建立的构架程序(framework)中包括一些基本类:主框架窗口CMainFrame:派生自CMDIFrameWnd;文档CM01Doc:派生自CDocument;浏览窗口CM01View:派生自CView;其中CM01Doc、CM01View和CMDIChildWnd由多文档模板CMultiDocTemplate联系在一起。在CM01App::InitInstance()函数中代码如下:

 1 BOOL CM01App::InitInstance()
 2 {
 3   ......
 4 
 5   CMultiDocTemplate*pDocTemplate;
 6   //CMultiDocTemplate用于MDI文档
 7   pDocTemplate=newCMultiDocTemplate(
 8   IDR_M01TYPE,//资源标识
 9   RUNTIME_CLASS(CM01Doc),
10   //文档类
11   RUNTIME_CLASS(CMDIChildWnd),
12   //标准MDI子窗口框架
13   RUNTIME_CLASS(CM01View));
14   //浏览视窗类
15   AddDocTemplate(pDocTemplate);
16   //为整个应用程序添加新模板
17   ......
18 }

  此时,数据Doc类仅与一种View类相关联,MDI每个子窗口显示的内容是一致的。如果用户希望不同的子窗口显示不同的文档,则需要分别建立新的资源项、新的文档类、新的View类,并且用新模板将他们与CMDIChildWnd联系起来即可。MFC框架程序将复杂的消息发送和接收机制隐藏起来,自动实现子窗口的调度安排。程序员只需设定自己的数据,并在各个View中重载OnDraw()函数,完成所需的绘制。

  然而在实际开发应用程序中,常常希望对某一类数据进行不同方式的显示,既可观察数值,又可有图形显示。这就要求同一种Doc类与多个View类相关联,而每个View类对应一个不同的MDI子窗口。CMultiDocTemplate的典型用法是建立独立的文档结构和View对象。而下面CMultiDocTemplate将使用同一文档和多个View类。

(1)用ClassWizard建立一新的View类:CM02View。

(2)建立新模板:

CMultiDocTemplate*pDocTemplate02=newCMultiDocTemplate(

  IDR_M01TYPE,//使用同一资源

  RUNTIME_CLASS(CM01Doc),//同一文档

  RUNTIME_CLASS(CMDIChildWnd),//标准MDI子窗口框架

  RUNTIME_CLASS(CM02View));//新View

  然后使用CApp::AddDocTemplate函数添加新模板。

  如果此时仍然在CM01App::InitInstance()函数中添加新模板,则构架程序会错误地认为程序支持两种文档类型,从而在编译产生的EXE文件执行时弹出对话框,要求用户选择文档类型。而实际上两种文档类型是一样的。

  为避免此种情况,可使用MFC开发者建议的方法:在前例情况下,首先,应在AppStudio中将字串资源IDR_M01TYPE复制为一个新字串资源IDR_M02TYPE。然后,删去字串资源IDR_M02TYPE中第二个后的字符串M01Document(该字串即为CDocTemplate::fileNewName项)。之后,用新资源IDR_M02TYPE来建立第二个模板。这样编译的EXE文件将不会弹出对话框。在研究MFC的源码之后,发现之所以弹出文档类型对话框,是由于CM01App::InitInstance()函数中调用了OnFileNew()函数。OnFileNew()函数检查文档模板数量;当不止一个模板时,则弹出对话框;待用户选择之后,按所选的文档类型建立MDI窗口。由于删去了第二个模板的fileNewName项,无法显示文档类型,就自动停止对话框,而将第一种类型作为缺省文档类型建立MDI窗口。

  在工程应用程序中,OnFileNew()函数一般只在程序初始化时调用一次(至于菜单File|New的响应,用户可接管处理),所以可以不在CMyApp::InitInstance()函数中添加新文档模板,躲过OnFileNew()函数的检查,而在需要的时候添加所需的文档模板,建立新的子窗口。这样既避免了文档类型对话框,又不必增加字串资源。

  一种简单的例子如下:第一个子窗口仍由构架程序自动建立;设定一个新的菜单项“新窗口(NewWindow)”,在CMainFrame中处理该菜单消息,消息响应函数中显示第二个子窗口。

 1 Void CMainFrame::OnNewWindow()
 2 {
 3      //添加新的文档模板
 4     Static CMultiDocTemplate*pDocTemplate_New;
 5     Static BOOLbChildCreated=FALSE;
 6     //标志,新窗口是否建立;如已建,将不重建
 7   if(bChildCreated==FALSE)
 8   {
 9     pDocTemplate_New=new CMultiDocTemplate(
10     IDR_M01TYPE,//使用同一资源
11     RUNTIME_CLASS(CM01Doc),
12     RUNTIME_CLASS(CMDIChildWnd),
13     //标准MDI子窗口框架
14     RUNTIME_CLASS(CM02View));
15     AfxGetApp()->AddDocTemplate(pdocTemplate_New);
16     //创建新的子窗口
17     CMDIChildWnd* pMDIActive=MDIGetActive();//获得当前活动子窗口的指针
18     CMpvDoc* pDoc=(CMpvDoc*)pMDIActive->GetActiveDocument();//获得文档指针
19     CMDIChildWnd* pNewFrame=(CMDIChildWnd*)(pDocTemplate_New->CreateNewFrame(pDoc,NULL));
20     //建立新的框架窗口
21     if(pNewFrame==NULL)
22     {
23       AfxMessageBox("新窗口不能建立",MB_OK,0);
24       return;//notcreated
25     }
26     pDocTemplate_New->InitialUpdateFrame(pNewFrame,pDoc);//显示窗口
27     MDITile(MDITILE_HORIZONTAL);//将多个窗口平铺
28     bChildCreated=TRUE;
29   }
30 }

  不同的View在OnDraw()函数中有各自的绘制代码,当数据更新时,只要调用CDocument::UpdateAllViews()函数,即可更新全部的MDI子窗口。

 

Collapse image概要

有时是想创建一个基于 CMultiDocTemplate 的窗口 (换句话说,CFrame/CDocument/CView 组合) 而无需使用由 CWinApp::OnFileNew 提供的机制。
例如,如果程序有多个文档模板,则 CWinApp::OnFileNew 将询问哪种类型的文档,以打开一个对话框提示用户。程序员可能已经知道哪种类型的 CMultiDocTemplate 来使用,并因此可能会不想提示用户,因为它将是不适当的应用程序的给定的上下文中。

Collapse image更多信息

未记录的 CMultiDocTemplate::OpenDocumentFile 函数假定应用程序最初创建与应用程序向导,用于创建一个新的基于 CMultiDocTemplate 窗口。有涉及的几个步骤:
  1. CWinApp 派生的类中添加的 CMultiDocTemplate 指针:
          class CMyApp : public CWinApp
          {
            ...
    
           public:
            CMultiDocTemplate* m_pDocTemplate;
    
            ...
    
          }						
    注意: 如果您计划使用多个文档类型,则必须创建每种文档类型的 CMultiDocTemplate 指针成员变量。
  2. 在对 CWinApp::InitInstance 的调用,从 AddDocTemplate 到调用中移除创建的 CMultiDocTemplate。将指针设置为指向新的 CMultiDocTemplate。使用指针调用 AddDocTemplate:
          BOOL CMyApp::InitInstance()
          {
            ...
    
            m_pDocTemplate = new CMultiDocTemplate(IDR_TEXTTYPE,
                                          RUNTIME_CLASS(CMyDoc),
                                          RUNTIME_CLASS(CMDIChildWnd),
                                          RUNTIME_CLASS(CMyView));
    
            AddDocTemplate(m_pDocTemplate);
    
            ...
    
          }						
  3. 使用指针调用 CMultiDocTemplate::OpenDocumentFile 具有空参数来创建新的窗口。对于此示例,假定在 CView 窗口中没有一个按钮。在按钮的 BN_CLICKED 处理,我们要创建一个基于 m_pDocTemplate 的窗口:
          void CMyView::OnNewWindowButtonClicked()
          {
              CMyApp* pApp = (CMyApp*)AfxGetApp();
              pApp->m_pDocTemplate->OpenDocumentFile(NULL);
          }						
    此技术可用于单文档界面 (SDI) 应用程序中创建一个基于 CSingleDocTemplate 的窗口。但是,它不是必要的。由于只有一个应用程序的文档模板,调用 OnFileNew() 将创建而不会提示用户输入的文档类型的新窗口。
  4. 参考文献 http://support.microsoft.com/kb/113257/zh-cn

相关文章:

  • apache2.2 虚拟主机配置
  • 【机器视觉与图像处理】基于MATLAB的角度计算
  • 【毕设进行时-工业大数据,数据挖掘】用C++对数据进行整改,修缮一下!
  • JDBC
  • 动画演示 Delphi 2007 IDE 功能[4] - 自定义界面
  • ASCSDK-------通用包接入文档(UNITY篇)
  • 内存管理[3]
  • Graphics 单元下的公用函数目录
  • 入口文件开始,分析Vue源码实现
  • hive可以drop所有表的bug fix
  • 标准化 归一化
  • MongoDB命令
  • 【转】nGrinder 简易使用教程
  • (备忘)Java Map 遍历
  • Linux vmstat命令实战详解
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • Apache Pulsar 2.1 重磅发布
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • opencv python Meanshift 和 Camshift
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • Python3爬取英雄联盟英雄皮肤大图
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • text-decoration与color属性
  • vue.js框架原理浅析
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 如何利用MongoDB打造TOP榜小程序
  • 入口文件开始,分析Vue源码实现
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • const的用法,特别是用在函数前面与后面的区别
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • #DBA杂记1
  • (1) caustics\
  • (27)4.8 习题课
  • (3)llvm ir转换过程
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转)Google的Objective-C编码规范
  • (转载)利用webkit抓取动态网页和链接
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET框架类在ASP.NET中的使用(2) ——QA
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • /3GB和/USERVA开关
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • @Service注解让spring找到你的Service bean
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [1204 寻找子串位置] 解题报告
  • [20170713] 无法访问SQL Server
  • [ACL2022] Text Smoothing: 一种在文本分类任务上的数据增强方法
  • [AIGC 大数据基础]hive浅谈
  • [C# WPF] 如何给控件添加边框(Border)?
  • [C# 开发技巧]如何使不符合要求的元素等于离它最近的一个元素
  • [c++] 单例模式 + cyberrt TimingWheel 单例分析