MFC里面基于对话框的程序和SDI程序的区别是?

Rance 2009-07-06 11:07:19
分别如何在基于对话框的程序和SDI程序上添加控件并实现数据访问呢?
...全文
63 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
cen_jin_long 2009-07-08
  • 打赏
  • 举报
回复
字义上来看,对话框就一个对话框。

文档,就像WORD那样,不过是单文档的。就只有一个工作区域。
FrightingGuo 2009-07-07
  • 打赏
  • 举报
回复
不就是连接数据库嘛?上面几位的想干嘛?
数据库连接有好几种啊 ADO DAO ODBC
楼主最基本的都没有掌握....
Rance 2009-07-07
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 sunshine502 的回复:]
你说的数据访问什么概念。。?
[/Quote]

就是这个意思:
有个文本框, 往里面输入个字符串, 比如SQL查询语句
然后弄个按钮, 点一下, 就连接数据库查, 然后把查询结果写在窗体上的一个表格里

这里就有数据交换了....
sunshine502 2009-07-07
  • 打赏
  • 举报
回复
你说的数据访问什么概念。。?
eeixy2000 2009-07-07
  • 打赏
  • 举报
回复
可以这样理解:
这是编程模型问题,你应该有面向对象概念:我们可以把属性、方法等封装到一个类中,这样使用起来方便,结构也很清晰。所以大家推崇面向对象技术;
现在编程序也需要更好的方法,我们也可以找到很多编程模式,其中有一种叫做MVC的,其核心是使用“模型-视图-控制”的思想,你可以去查阅相关资料作进一步了解;
回到你的问题:微软在VS6中给出了几个典型应用框架:对话框应用、SDI应用、MDI应用,其中对话框应用主要用在一些小型Project中,在这些小型Project中涉及到业务模型相对简单,控制起来也很方便,另一种则是SDI应用,它不但封装了“视图”(用于展现你的数据),还给出了包装数据的类(即CDocument),在CView类和CDocument类之间自动封装了相互之间的应用,这样就方便了你在视图类中访问文档类中的数据,而且也方便了在视图类中访问文档类,当然MDI就更复杂一些(主要是用于展现数据的视图功能)
shengmingzhibei 2009-07-07
  • 打赏
  • 举报
回复
看了大家的帖子,我对MFC的学习一下有信心了~
dengxuxing 2009-07-07
  • 打赏
  • 举报
回复
楼上说的不错,如果界面不是很大时,用对话框方便
qingfeng5211 2009-07-07
  • 打赏
  • 举报
回复
SDI更强大吧,视图类可以从多个类继承,比如从CFormView继承的话,就可以直接放控件上去,另外它和文档类关联
对于数据的存贮就更方便了。特别是对象的存贮。
基于对话框适合做一些简单的控制界面。要添加工具栏,状态栏都比较麻烦。
glacierful 2009-07-07
  • 打赏
  • 举报
回复
9楼的是正解。

一般应用我感觉对话框可以替换SDI,我反正是用对话框放菜单、工具栏、状态栏什么的。
SDI还一直没有过,以后有时间也试试。
Rance 2009-07-07
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 FrightingGuo 的回复:]
不就是连接数据库嘛?上面几位的想干嘛?
数据库连接有好几种啊 ADO DAO ODBC
楼主最基本的都没有掌握....
[/Quote]

哦, 我的问题不在连接数据库.
我只是在用MYSQL, 连接MYSQL数据库的功能我已经实现在一个类里面了.
只是界面怎么弄不晓得
emyueguang 2009-07-07
  • 打赏
  • 举报
回复
我也在学MFC,好像是双击一下那个按钮,然后就会有相应的消息对应的函数出来,之后就是填写那个函数就行了,比如你想点击一个按键,实现你说的那个功能,加一些代码,像先获取窗口的字符串将它解析成SQL语句,再用这个SQL语句查询数据库,将数据显示到窗口中,就这个样子咯,详细代码MFC的我不会写,我的思路就是这个样子的
eyoung000 2009-07-07
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 eeixy2000 的回复:]
可以这样理解:
这是编程模型问题,你应该有面向对象概念:我们可以把属性、方法等封装到一个类中,这样使用起来方便,结构也很清晰。所以大家推崇面向对象技术;
现在编程序也需要更好的方法,我们也可以找到很多编程模式,其中有一种叫做MVC的,其核心是使用“模型-视图-控制”的思想,你可以去查阅相关资料作进一步了解;
回到你的问题:微软在VS6中给出了几个典型应用框架:对话框应用、SDI应用、MDI应用,…
[/Quote]

寄生ing:
一直在用基于对话框的,SDI应用不知怎么下手。。指教下~
clever101 2009-07-06
  • 打赏
  • 举报
回复
引用楼主之前的帖子:需要用MFC来做东西,想找个师傅....
有没有需要提高英语水平的程序设计高手?


成交。楼主,我是比较符合条件的。我也在北京海淀工作,周末有空可以当面指点你一下。MFC编程水平不敢称高手,但自认还能教一下人的。

联系方式:
E_mail:clever101@gmail.com
QQ:258156036
博客:http://blog.csdn.net/clever101
Rance 2009-07-06
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 clever101 的回复:]
引用 1 楼 healer_kx 的回复:
你肯定是在校学生吧,而且估计是研究生。。。。


呵呵,也有可能是博士生啊!
[/Quote]

哥儿几个也不回答我的问题, 净埋汰我了, 唉
clever101 2009-07-06
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 healer_kx 的回复:]
你肯定是在校学生吧,而且估计是研究生。。。。
[/Quote]

呵呵,也有可能是博士生啊!
Rance 2009-07-06
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 healer_kx 的回复:]
你肯定是在校学生吧,而且估计是研究生。。。。
[/Quote]

这个.....
很无语
healer_kx 2009-07-06
  • 打赏
  • 举报
回复
你肯定是在校学生吧,而且估计是研究生。。。。
目 录 译者序 前言 第一部分 基础知识 第1章 窗口 2 1.1 窗口和API环境 2 1.1.1 三种类型窗口 2 1.1.2 客户区和非客户区 3 1.2 窗口和MFC环境 4 1.3 怎样应用MFC创建一个窗口 5 1.4 怎样使用MFC销毁一个窗口 9 1.4.1 捆绑到一个已有的窗口 9 1.4.2 窗口类 10 1.4.3 窗口进程 10 1.5 怎样使用MFC创建一个窗口类 11 1.5.1 使用AfxRegisterWndClass () 函数注册一个窗口类 11 1.5.2 使用AfxRegisterClass ()函数 创建一个窗口类 12 1.6 怎样销毁一个MFC窗口类 14 1.7 厂商安装的窗口类 14 1.8 其他类型窗口 15 1.9 桌面窗口 16 1.10 小结 16 第2章 类 18 2.1 基类 18 2.1.1 CObject 18 2.1.2 CCmdTarget 19 2.1.3 CWnd 19 2.2 应用程序、框架、文档和视图类 19 2.2.1 CWinApp(O/C/W) 20 2.2.2 CView (O/C/W) 21 2.3 其他用户界面类 22 2.3.1 通用控件类 23 2.3.2 菜单类 23 2.3.3 对话框类 24 2.3.4 控制条类 24 2.3.5 属性类 25 2.4 绘图类 25 2.4.1 设备环境类 25 2.4.2 图形对象类 25 2.5 文件类 26 2.6 数据库类 26 2.6.1 ODBC类 26 2.6.2 DAO类 27 2.7 数据集类 27 2.8 其他数据类 27 2.9 通信类 28 2.10 其他类 29 2.11 小结 31 第3章 消息处理 32 3.1 发送或寄送一个消息 32 3.1.1 发送一个消息 32 3.1.2 寄送一个消息 32 3.1.3 发送一个消息与寄送一个消息 的比较 32 3.2 怎样使用MFC发送一个消息 33 3.3 怎样用MFC寄送一个消息 33 3.4 三种类型的消息 34 3.4.1 窗口消息 34 3.4.2 命令消息 34 3.4.3 控件通知 34 3.5 MFC怎样接收一个寄送的消息 36 3.6 MFC怎样处理一个接收到的消息 36 3.7 处理用户界面的对象 44 3.8 创建自定义窗口消息 45 3.8.1 静态分配的窗口消息 45 3.8.2 动态分配的窗口消息 46 3.9 重定向消息 47 3.9.1 子分类和超分类 47 3.9.2 用MFC子分类窗口 48 3.9.3 重载OnCmdMsg ( ) 49 3.9.4 使用SetWindowsHookEx ( ) 49 3.9.5 使用SetCapture ( ) 49 3.9.6 专有的消息泵 50 3.10 小结 50 第4章 绘图 51 4.1 设备环境 51 4.2 在MFC环境中创建一个设备环境 52 4.2.1 屏幕 52 4.2.2 打印机 53 4.2.3 内存 54 4.2.4 信息 54 4.3 绘图例程 55 4.3.1 画点 55 4.3.2 画线 55 4.3.3 画形状 55 4.3.4 形状填充和翻转 55 4.3.5 滚动 56 4.3.6 绘制文本 56 4.3.7 绘制位图和图标 56 4.4 绘图属性 56 4.4.1 设备环境属性 57 4.4.2 画线属性 58 4.4.3 形状填充属性 58 4.4.4 文本绘制属性 58 4.4.5 映像模式 59 4.4.6 调色板属性 62 4.4.7 混合属性 62 4.4.8 剪裁属性 63 4.4.9 位图绘制属性 64 4.5 元文件和路径 65 4.5.1 元文件 65 4.5.2 路径 66 4.6 颜色和调色板 66 4.6.1 抖动色 67 4.6.2 未经抖动色 67 4.6.3 系统调色板 67 4.6.4 使用系统调色板 68 4.6.5 动画色 71 4.7 控制什么时候在哪里绘图 71 4.7.1 处理WM_PAINT 71 4.7.2 只绘制被无效化的区域 72 4.7.3 处理WM_DRAWITEM 72 4.7.4 在其他时间绘图 73 4.8 小结 74 第二部分 用户界面实例 第5章 应用程序与环境 76 5.1 例1 规划MFC应用程序 76 5.2 例2 用AppWizard创建一个MFC 应用程序 79 5.3 例3 用ClassWizard创建一个类 83 5.4 例4 初始化应用程序屏幕 84 5.5 例5 保存应用程序屏幕 86 5.6 例6 处理命令行选项 88 5.7 例7 动态改变应用程序图标 91 5.8 例8 提示用户优先选项 93 5.9 例9 保存和恢复用户优先选项 97 5.10 例10 终止应用程序 100 5.11 例11 创建一个启动窗口 101 第6章 菜单 107 6.1 例12 使用菜单编辑器 107 6.2 例13 添加一个菜单命令处理函数 109 6.3 例14 根据当前可视文档动态改 变菜单 110 6.4 例15 启用和禁用菜单命令 111 6.5 例16 复选标记菜单命令 112 6.6 例17 单选标记菜单命令 113 6.7 例18 动态修改菜单 114 6.8 例19 动态修改系统菜单 116 6.9 例20 触发一个菜单命令 117 6.10 例21 创建弹出式菜单 117 第7章 工具栏和状态栏 120 7.1 例22 使用工具栏编辑器 120 7.2 例23 启用和禁用工具栏按钮 122 7.3 例24 为工具栏按钮添加字 123 7.4 例25 非标准工具栏大小 128 7.5 例26 保持工具栏按钮按下 129 7.6 例27 保持工具栏按钮组中 一个按钮按下 130 7.7 例28 为工具栏添加非按钮控件 131 7.8 例29 修改应用程序的状态栏 136 7.9 例30 更新状态栏窗格 138 7.10 例31 为状态栏添加其他控件 139 第8章 视图 145 8.1 例32 滚动视图 145 8.2 例33 改变鼠标光标形状 147 8.3 例34 沙漏光标 148 8.4 例35 窗体视图 149 8.5 例36 列表视图 152 8.6 例37 动态分割一个视图 163 第9章 对话框和对话条 166 9.1 例38 使用对话框编辑器 166 9.2 例39 创建一个对话框类 168 9.3 例40 模式对话框 170 9.4 例41 无模式对话框 171 9.5 例42 在无模式对话框的控件间 切换焦点 172 9.6 例43 对话框中的动画 173 9.7 例44 消息框 174 9.8 例45 对话条 176 第10章 控件窗口 182 10.1 例46 在任意位置创建一个控 件窗口 182 10.2 例47 用子分类定制一个通用 控件窗口 183 10.3 例48 用超分类定制一个通用 控件窗口 188 10.4 例49 在按钮上放置位图 190 10.5 例50 动态填充一个组合框 192 10.6 例51 排序一个列表控件 194 10.7 例52 分隔线控件 196 第11章 绘图 198 11.1 例53 绘制图形 198 11.2 例54 绘制文本 201 11.3 例55 从任意位置装入一个图 标并绘制 203 11.4 例56 从任意位置装入一个位 图和绘制一个位图 204 11.5 例57 从文件中创建一个位图 206 11.6 例58 创建一个自绘位图 211 第三部分 内部处理实例 第12章 消息 215 12.1 例59 添加消息处理函数或重 载MFC类 216 12.2 例60 添加命令范围消息处理函数 219 12.3 例61 重定向命令消息 221 12.4 例62 创建自己的窗口消息 222 第13章 文件、串行化和数据库 225 13.1 例63 访问二进制文件 225 13.2 例64 访问标准I/O文件 227 13.3 例65 访问内存文件 228 13.4 例66 在数据类中实现串行化 229 13.5 例67 串行化SDI或MDI文档 235 13.6 例68 按要求串行化 240 13.7 例69 透明地更新串行化的文档 242 13.8 例70 串行化多态类 246 13.9 例71 串行化数据集 248 13.10 例72 访问ODBC数据库 252 13.11 例73 访问DAO数据库 257 第14章 杂类 263 14.1 例74 剪切、拷贝和粘贴文本 数据 263 14.2 例75 剪切、拷贝、粘贴多信 息文本数据 268 14.3 例76 剪切、拷贝和粘贴二进制 数据 273 14.4 例77 数组函数 280 14.5 例78 列表函数 281 14.6 例79 映像函数 283 14.7 例80 系统键盘输入 285 14.8 例81 时间 288 第四部分 打包实例 第15章 库 291 15.1 例82 静态链接C/C++库 291 15.2 例83 动态链接C/C++库 295 15.3 例84 动态链接MFC扩展类库 300 15.4 例85 资源库 303 第五部分 附录 附录A 控件窗口风格 305 附录B 消息、控件通知和消息映像宏 323 附录C 访问其他应用程序类 328 附录D 开发中注意事项 330 附录E MFC快速参考指南 339
什么是句柄? 句柄,是整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息,但是句柄不是一个指针,程序不能利用句柄来直接阅读文件中的信息。如果句柄不用在I/O文件中,它是毫无用处的。 句柄是Windows用来标志应用程序中建立的或是使用的唯一整数,Windows使用了大量的句柄来标志很多对象。 一、MFC AppWizard 1、MFC(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发工作所开发的一套C++ 类的集合,是一套面向对象的函数库,以类的方式提供给用户使用 2、MFC AppWizard是一个辅助我们生成源代码的向导工具,它可以帮助我们自动生成基于MFC框架的源代码 二、基于MFC程序框架剖析 1、MFC程序的ClassView标签页(图) 2、继承关系 (1)CMainFrame继承于CFrameWnd (2)CTestApp继承于CWinApp (3)CTestDoc继承于CDocument (4)CTestView继承于CView 注:CFrameWnd和CView都继承于CWnd 3、CWnd类是MFC中一个非常重要的类,它封装了与窗口相关的操作 4、MFC类的简化组织结构图(图) 5、MFC程序也有一个WinMain函数,程序是在编译时,由链接器将它链接到程序中 6、MFC程序具有一个CTestApp类的全局对象theApp,在MFC程序运行时,程序执行的顺序为:theApp全局对象定义 处->CTestApp构造函数->WinMain函数 7、对于普通的VC++控制台程序,无论全局变量还是全局对象,程序运行时,在加载main函数之前,就已经为它们 分配了内存空间。对于一个全局对象来说,此时就会调用该对象的构造函数,构造该对象,并进行初始化操作 8、实例句柄与全局对象 (1)对于Win32 SDK程序,应用程序的实例是由实例句柄(WinMain函数的hInstance参数)来标识的 (2)对于MFC程序,应用程序的实例是由全局对象(每一个MFC程序有且仅有一个从应用程序类CWinApp派生的类, 如CTestApp,它实例化theApp全局对象)来标识的 9、基类构造函数中this指针的指向问题 在构造子类对象时,会自动调用父类的构造函数,此时在父类的构造函数中的this指针所指向的是子类对象地址 10、AfxWinMain函数 MFC程序的WinMain函数是通过调用AfxWinMain函数来完成它的功能的 注:Afx前缀的函数代表应用程序框架(Application Framework)函数,它们可以在程序的任何地方被调用 11、CTestApp::InitInstance函数 在AfxWinMain函数中,通过调用InitInstance函数来完成MFC内部管理方面的工作 12、AfxEndDeferRegisterClass函数 MFC提供了一些默认的标准窗口类,我们只需要选择所需的窗口类就行。然后,调用AfxEndDeferRegisterClass 函数来注册窗口类 13、CMainFrame::PreCreateWindow函数 MFC程序具有两个窗口(框架窗口和视类窗口),在框架窗口产生之前会调用PreCreateWindow函数 14、CWnd::CreateEx函数 在MFC程序中,窗口的创建是由CreateEx函数实现的 15、CWnd::CreateWindowEx函数 主要作用是当修改了CreateEx函数的CREATESTRUCT参数时,CreateWindowEx函数会根据参数发生的相应变化来创 建一个符合我们要求的窗口 注:MFC中后缀名为Ex的函数都是扩展函数 16、CMainFrame::ShowWindow函数和CMainFrame::UpdateWindow函数 用于显示应用程序框架窗口和更新这个窗口 17、CWinThread::Run函数和CWinThread::PumpMessage函数 用于完成消息循环 18、DefWindowProc函数 默认的窗口过程,但MFC程序对消息的处理实际上是通过消息映射机制来完成的 19、MFC程序的运行过程 (1)首先利用全局应用程序对象theApp启动应用程序 (2)调用全局应用程序对象的构造函数,从而就会调用其基类CWinApp的构造函数,以完成应用程序的一些初始化 (3)进入WinMain函数 (4)进入消息循环 20、MFC程序的主要过程 theApp-> CTestApp::CTestApp构造函数-> CWinApp::CWinApp构造函数-> _tWinMain(WinMain函数的宏)-> AfxWinMain函数-> CTestApp::InitInstance函数-> AfxEndDeferRegisterClass函数-> CMainFrame::PreCreateWindow函数-> CFrameWnd::PreCreateWindow函数-> AfxDeferRegisterClass(AfxEndDeferRegisterClass函数的宏)-> CFrameWnd::Create函数-> CWnd::CreateEx函数-> CMainFrame::PreCreateWindow函数-> CWnd::CreateEx函数-> CMainFrame::ShowWindow函数-> CMainFrame::UpdateWindow函数-> CWinThread::Run函数-> CWinThread::PumpMessage函数 21、框架窗口(整个应用程序外框所包括的部分)是视类窗口(框架窗口中空白的地方)的一个父窗口 22、MFC提供了一个文档/视类的结构,文档是指CDocument类,视类是指CView类。前者用于数据的存储和加载, 后者用于数据的显示和修改 23、框架对象、文档对象和视类对象是通过一个单文档模板指针来有机地组织在一起,并利用AddDocTemplate函数 把这个单文档模板添加到文档模板中,从而把这三个类组织成为一个整体 24、MFC程序的CAboutDlg类继承于CDialog类,用于为用户提供一些与程序相关的帮助信息 三、窗口类、窗口类对象与窗口 1、以“::”开始的函数是一个全局函数,表示调用的是Platform SDK的函数 2、如果我们关闭了一个窗口,这个窗口就销毁了,那么该窗口对应的C++窗口类对象销毁了吗? (1)当一个窗口销毁时,它会调用CWnd::DestroyWindow函数,该函数销毁窗口后,将CWnd::m_hWnd设为NULL (2)窗口的生命周期和C++窗口类对象的声明周期不是一致的。当一个窗口销毁时,与C++窗口类对象没有关系,它 们之间的纽带仅仅在于这个C++窗口类内部的成员变量m_hWnd,该变量保存了与这个C++窗口类对象相关的哪个窗口 的句柄 (3)但是,当C++窗口类对象销毁时,与之相关的窗口也将销毁,因为它们之间的纽带m_hWnd已经断了 3、示例---在窗口中显示按钮 (1)CButton按钮类继承于CWnd (2)对于一个CButton对象,在定义之后就可以使用了;但是,如果要显示这个按钮的话,还需调用 CButton::Create函数,把按钮窗口与CButton对象关联起来 (3)MFC程序的窗口创建时都会产生WM_CREATE消息,该消息通过OnCreate函数来捕获。对于框架窗口来说,MFC直 接把OnCreate函数提供到了CMainFrame中;而在视类窗口中没有提供该函数,如需使用,要用户自行添加 (4)通常对MFC程序的操作,都是在CTestView视类窗口中进行的 (5)在窗口创建之后,要显示该窗口可以通过调用ShowWindow函数或指定窗口风格为WS_VISIBLE来实现 (6)实现过程 A:在CTestView类中,添加CButton类型的私有成员m_btn B:在CTestView类中,添加WM_CREATE消息的OnCreate处理函数 C:在CTestView类中,通过GetParent函数可以获得CMainFrame框架窗口对象的指针 D:实现一(在视类窗口中通过ShowWindow函数显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); m_btn.ShowWindow(SW_SHOWNORMAL); return 0: } E:实现二(在视类窗口中通过WS_VISIBLE风格显示窗口) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); return 0: } F:实现三(在框架窗口中显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),GetParent(),123); return 0: } 即便是基于MFC的应用程序,建立窗口类也是会遵循如下的过程: 设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口过程函数处理。下面就剖析一下在MFC中是如何完成上述过程的。 (1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于WinMain函数进行处理。 (2)WinMain函数体在APPMODUL.CPP文件中,定义如下: extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 其中#define _tWinMain WinMain (3)AfxWinMain函数体在WINMAIN.CPP文件中,里面有如下两句话: CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); 其实这里得到的这两个指针都是指向全局的对象theApp的; 接下来有函数调用pThread->InitInstance(),根据多态性,会调用CXXXApp类中的InitInstance()函数。该函数很重要,在对该函数的调用中就会完成:设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口。 接下来,该函数中会继续调用pThread->Run(),这就完成了:消息循环->消息路由到窗口过程函数处理。 (4)进入CXXXApp::InitInstance()函数体中,对于单文档应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口类->注册窗口类->生成窗口。 再接下来就会调用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();这就完成了:显示窗口->更新窗口。 (5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)中会进入到如下的case分支:case CCommandLineInfo::FileNew: if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)) (6)进入函数CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); (7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用 case AfxSig_vv: // normal command or control notification ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED ASSERT(pExtra == NULL); (pTarget->*mmf.pfn_COMMAND)(); (8)进入CWinApp::OnFileNew(),调用m_pDocManager->OnFileNew();这个函数很特殊,它本身是个消息响应函数,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,由于命令消息可以被CCmdTarget类及其派生类来捕获,而CWinApp是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会首先进入这个函数,然后再依次执行下去,最后就会执行到pDocument->OnNewDocument()中,往往我们会对这个函数不解,不知道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会自动的产生这个消息呢?这是因为在CXXXXApp::InitInstance()函数中有“CCommandLineInfo cmdInfo;”这个类的构造函数是这样的:CCommandLineInfo::CCommandLineInfo() { m_bShowSplash = TRUE; m_bRunEmbedded = FALSE; m_bRunAutomated = FALSE; m_nShellCommand = FileNew; },因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支中,就相当于产生了这样一个FileNew的消息。同理对于ID为ID_FILE_OPEN(在CWinApp::OnFileOpen()中响应)、ID_FILE_SAVE(在CDocument::OnFileSave()中响应)等等在MFC向导为我们生成的单文档类中找不到消息响应的入口时,其实都是在基类CWinApp或者CDocument类中进行了响应。对于CXXXXDoc::Serialize(CArchive& ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。 (9)进入CDocManager::OnFileNew(),CDocManager类有一个成员变量是CPtrList m_templateList;该变量保存了一个文档模版链表指针,在CDocManager::OnFileNew()函数体中会调用CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表中的头,也就是第一个文档模版,后面就会用得到的这个指针去调用pTemplate->OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表中是否只有一项,如果链表中保存了多个文档模版,则会弹出一个对话框,来让我们选择到底是使用哪一套文档模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate::OpenDocumentFile的函数体内呢,这是因为CDocTemplate类中的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate类又是从CDocTemplate类派生出来的,并且实现了该函数,因此就会进入到子类的函数体中了。 (10)进入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible),先调用CreateNewDocument()创建文档类,再调用pFrame = CreateNewFrame(pDocument, NULL);创建框架类和视图类,从这里也可以看出MFC体系结构中文档、框架、视图“三位一体”的模式,在这一个函数中同时创建三个类;再会调用pDocument->OnNewDocument();因此就会进入到子类的文档类中的pDocument->OnNewDocument()中了。 (11)进入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),调用if (!pFrame->LoadFrame(m_nIDResource, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles NULL, &context)) (12)进入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); (13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口类->注册窗口类。MFC通过给我们提供好一些已经订制好的窗口类,我们不需要自己再设计窗口类,只需要到那些订制好的窗口类“仓库”中寻找一种适合我们需要的窗口类就可以了,然后通过AfxRegisterClass函数注册窗口类。还需要说明的是,再后续的跟踪过程中,我们会发现还会进入到AfxEndDeferRegisterClass函数中进行设计和注册窗口类,这主要是因为单文档应用程序比较特殊,它提前通过这样的一种途径进行了窗口类的设计和注册步骤,其实是应该在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数的调用中完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口类的窗口过程函数都是DefWindowProc,这一点在后面的跟踪中可以看到,每次生成窗口之后都会调用几次DefWindowProc函数。也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。 (14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource); (15)进入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),调用PreCreateWindow(cs); (16)进入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd::PreCreateWindow(cs) (17)进入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次设计和注册窗口类 (18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) (19)进入BOOL CFrameWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext),调用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext)) (20)BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),调用if (!PreCreateWindow(cs)) ,接下来调用HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);好了,终于让我们找到生成窗口的地方了——函数::CreateWindowEx! (21)进入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),调用if (CFrameWnd::OnCreate(lpCreateStruct) == -1) (22)进入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),调用return OnCreateHelper(lpcs, pContext); (23)进入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (CWnd::OnCreate(lpcs) == -1) (24)进入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),调用return (int)Default(); (25)进入LRESULT CWnd::Default(),调用return DefWindowProc(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam); (26)进入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam); (27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (!OnCreateClient(lpcs, pContext)) (28)进入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL) (29)进入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),调用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext)) (30)进入BOOL CWnd::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext); (31)进入BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。 调用的顺序是这个样子的:PreCreateWindow(cs)->BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->... 写到这里,基本上就清楚了,中间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程函数,每次在调用::CreateWindowEx(...)函数后都会调用一些窗口过程函数,然后再去调用该窗口类对应的OnCreate函数,其实在调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame类的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之类的东西,当然我们也可以在CXXXView类的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如按钮之类的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们可以在创建的时候,给窗口风格中添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函数,因为窗口在创建的时候就可见了。 总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册生成视图类OnCreate函数后面用户添加的、用Create来准备创建的窗口,然后再注册生成框架类的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建的窗口类,最后在回到应用类的初始化的函数体中,调用框架类的显示和更新函数,然后再进入由框架类定义的窗口的消息循环中。 消息循环的过程是这个样子的: (1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)函数中的pThread->Run() (2)进入int CWinApp::Run(),调用return CWinThread::Run(); (3)进入int CWinThread::Run(),调用if (!PumpMessage()) (4)进入BOOL CWinThread::PumpMessage(),调用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) (5)回到BOOL CWinThread::PumpMessage(),调用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur); (6)回到int CWinThread::Run(),调用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); (7)再重复(4)-(6)的步骤 下面给出int CWinThread::Run()中消息循环的部分代码: do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); 这段代码其实本质上与我们基于Win32 SDK手写的代码: //消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { //简单的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该函数就不能产生WM_CHAR消息。 TranslateMessage(&msg); ::DispatchMessage(&msg); } 是一致的。 1,寻找WinMain人口: 在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。 路径:MFC|SRC|APPMODUL.CPP: _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 注意:(#define _tWinMain WinMain) 2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。 所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){} 说明:每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。 3,通过构造应用程序对象过程中调用基类CWinApp的构造函数,在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。 CWinApp构造函数:MFC|SRC|APPCORE.CPP CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下: (在CWinApp类定义中, CWinApp(LPCTSTR lpszAppName = NULL); ) 注意:CWinApp()函数中: pThreadState->m_pCurrentWinThread = this; pModuleState->m_pCurrentWinApp = this (this指向的是派生类CTEApp对象,即theApp) 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){} 4,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。 AfxWinMain()函数路径:MFC|SRC|WINMAIN.CPP: 在AfxWinMain()函数中: CWinApp* pApp = AfxGetApp(); 说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。 //_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() // { return afxCurrentWinApp; } 调用pThread->InitInstance() 说明:pThread也指向theApp,由于基类中virtual BOOL InitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生类CTEApp的InitInstance()函数。 nReturnCode = pThread->Run(); 说明:pThread->Run()完成了消息循环。 5,注册窗口类:AfxEndDeferRegisterClass(); AfxEndDeferRegisterClass()函数所在文件:MFC|SRC|APPCORE.CPP BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...} 说明:设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。 调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//进入程序 ->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父类InitInstance虚函数;->CTEApp::InitInstance()//子类实现函数;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreateWindow中进行注册)//之后进入创建窗口阶段(以下再不做调试) 6,PreCreateWindow()://主要是注册窗口类 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; return TRUE; } 说明: CFrameWnd::PreCreateWindow()函数所在文件:MFC|SRC|WINFRM.CPP BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) { if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); //判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册 cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background //把注册后的窗口类名赋给cs.lpszClass } if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4) cs.style |= FWS_PREFIXTITLE; if (afxData.bWin4) cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE; } 其中: virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。 #define VERIFY(f) ASSERT(f) #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass) define AFX_WNDFRAMEORVIEW_REG 0x00008 const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中,定义为全局数组。 //#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView") 7,创建窗口: Create()函数路径:MFC|SRC|WINFRM.CPP: CFrameWnd::Create(...){ ... CreateEx(...);//从父类继承来的,调用CWnd::CreateEx(). ... } CWnd::CreateEx()函数路径:MFC|SRC|WINCORE.CPP BOOL CWnd::CreateEx(...){ ... if (!PreCreateWindow(cs))//虚函数,如果子类有调用子类的。 { PostNcDestroy(); return FALSE; } ... HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); ... } 说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子类有调用子类的。 HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); typedef struct tagCREATESTRUCT { // cs LPVOID lpCreateParams; HINSTANCE hInstance; HMENU hMenu; HWND hwndParent; int cy; int cx; int y; int x; LONG style; LPCTSTR lpszName; LPCTSTR lpszClass; DWORD dwExStyle; } CREATESTRUCT; 8,显示和更新窗口: CTEApp类,TEApp.cpp中 m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口 m_pMainWnd->UpdateWindow();//更新窗口 说明: class CTEApp : public CWinApp{...} class CWinApp : public CWinThread{...} class CWinThread : public CCmdTarget { ... public: CWnd* m_pMainWnd; ... ... } 9,消息循环: int AFXAPI AfxWinMain() { ... // Perform specific initializations if (!pThread->InitInstance()){...} //完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。 nReturnCode = pThread->Run(); //继承基类Run()方法,调用CWinThread::Run()来完成消息循环 ... } //////////////////////////////////////////////////////////////// CWinThread::Run()方法路径:MFC|SRC|THRDCORE.CPP int CWinThread::Run() { ... // phase2: pump messages while available do//消息循环 { // pump message, but quit on WM_QUIT if (!PumpMessage())//取消息并处理 return ExitInstance(); ... } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); ... } 说明: BOOL PeekMessage(,,,,)函数说明 The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure. If a message is available, the return value is nonzero. If no messages are available, the return value is zero. ///////////////////////////////////////////////////////////// BOOL CWinThread::PumpMessage() { ... if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息 {...} ... // process this message if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { ::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换 ::DispatchMessage(&m_msgCur);//分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。) } return TRUE; } 9,文档与视结构: 可以认为View类窗口是CMainFram类窗口的子窗口。 DOCument类是文档类。 DOC-VIEW结构将数据本身与它的显示分离开。 文档类:数据的存储,加载 视类:数据的显示,修改 10,文档类,视类,框架类的有机结合: 在CTEApp类CTEApp::InitInstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。 ... CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTEDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CTEView)); AddDocTemplate(pDocTemplate);//增加到模板

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧