如何优雅的写UI——(1)MFC六大核心机制-程序初始化
很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识就可以做出各种窗口程序,但喜欢钻研的朋友肯定想知道,到底微软帮我们做了些什么,让我们在它的框架下可以简单的写程序。本文开始就跟大家分享一位同行前辈写的MFC核心机制分析(稍作整理),语言朴实易懂,在读完此深入浅析的剖析系列后,相信留给大家的是对MFC运行机制的深入理解。
MFC六大核心机制概述
我们选择了C++,主要是因为它够艺术、够自由,使用它我们可以实现各种想法,而MFC将多种可灵活使用的功能封装起来,我们岂能忍受这种“黑盒”操作?于是研究分析MFC的核心机制成为必然。
首先,列出要讲的MFC六大核心机制:
1、MFC程序的初始化。
2、运行时类型识别(RTTI)。
3、动态创建。
4、永久保存。
5、消息映射。
6、消息传递。
(一)MFC程序的初始化
一般情况下,采用文档/视图结构的应用程序至少应由以下对象组成:应用程序对象(CWinApp类派生的对象),框架窗口对象(CFrameWnd类派生的对象),文档对象(CDocument类派生的对象),视图对象(CView类派生的对象)。另外,还必须有一个负责管理文档和视图的文档模板类(CDocTemplate)。其中的主角是CDocument类(文档类)和 CView类(视图类),这就是文档/视图结构的由来。各类的作用分别介绍如下:
1.CWinApp
CwinApp (应用程序类) 提供了用户与 Windows 应用程序之间进行交流的界面。在实例化该类对象后,这个对象自动地把自身与 Widnows 系统建立联系,接收 Windows 传送的消息,并交给程序中相应的对象去处理,免去了程序员许多的工作,使得开发 Windows 序变得简单方便。
这个类中有一个重要的成员函数:InitInstance(),在 Windows 环境下,可以运行同一程序的多个实例,函数 InitInstance() 的作用是在生成的一个新的实例的时候,完成一些初始化的工作。另外还有一个函数InitApplication(),与前者的区别是它"每一个程序只调用一次",而 InitInstance() 是"每一个例程调用一次"。
2.CFrameWnd
CFrameWnd (框架窗口类) 是应用程序的框架窗口。所谓框架窗口是指包括菜单、工具栏、状态栏和窗口客户区的整个应用程序的主窗口,相当于简单应用程序框架中所提到的主窗口。在 MFC 程序中,一般并不需要经常对 CFrameWnd 类进行操作,更多的是对视窗类进行操作,达到对程序中的数据进行编辑的目的。
3.CView
CView (视图类) 派生于 Cwnd 类,用于管理文档/视图结构中的窗口客户区,这个窗口在文档/视图结构中称为视图。视图类专门用于对应用程序的数据进行显示,在视图类中有一个很重要的函数 OnDraw(),OnDraw()函数是用于进行应用程序数据显示的函数,一般在派生类中要重写这一个函数。在文档/视图结构中,重写的OnDraw()函数首先清空客户区窗口,然后再在窗口上绘制客户需要的内容,也就是说,OnDraw() 函数将集中管理所有的数据显示工作。
4.CDocument
CDocument (文档类) 虽然视图类负责应用程序数据的显示,但应用程序的数据一般不直接由视图类管理,而是作为文档类(CDocument)的数据成员,由文档类来进行集中管理,而且文档类将直接与磁盘相联系,把文档类中的数据存盘,或从磁盘中取出存盘的数据。视图类用 OnDraw() 函数展示数据,但应用程序的数据却存放在文档类中,视图类的函数 GetDocument() 的返回值就是指向文档类的指针,通过这个指针就可以访问 到文档类中的公有数据成员。文档类的数据要存盘或取盘要与磁盘进行数据传递,可以用 CFile 类结合CFileDialog 类实现。在文档/视图结构中,通过文档类中的序列化函数 Serialize() 就可以很简单的完成数据存取任务。文档/视图结构中,数据的传输过程如下图所示:
5.CDocTemplate
CDocTemplate(文档模板类)的作用是连接文档/视图结构中文档类,视图类和框架窗口类之间的关系,文档类,视图类和框架窗口类之间的关系是在文档模板类中建立起来的,同时文档模板类还加载了菜单以及与菜单资源使用的 ID 等资源。具体来说,在 CWinApp 派生类的 InitInstance() 函数中建立了文档模板类 CDocTemplate,并用文档模板类连接资源、框架窗口、文档和视图。
6.类层次结构
继承类图,左边为父类,右边为派生类。实线框中的类只有 CObject 在 "afx.h" 中,其余都在 "afxwin.h" 中,虚线框中全部是用户自己定义的派生类。
7.使用控制台程序模拟 MFC 初始化
注意!是使用控制台应用程序(Win32 Console Application)模拟而不是 MFC 程序!另外不用急着 copy 代码,我在文章底部附录一给出了代码下载链接和使用方法。
假设新建一个控制台应用程序叫"My",首先自然是编写上面五个重要类的派生类,其中 CDocTemplate 用于管理其他类的对象,可以被直接使用,这里就暂且不讨论了(它的派生类被声明在"afxwin.h"中)。于是创建四个头文件并声明其他类的派生类。
//My.h
class CMyApp : public CWinApp
{
public:
CMyApp();
virtual BOOL InitInstance(); //覆盖
};
//MyDoc.h class CMyDoc : public CDocument
{
public :
CMyDoc();
};
//MyFrame.h class CMyFrame : public CFrameWnd
{
public:
CMyFrame();
};
//MyView.h class CMyView : public CView
{
public:
CMyView();
};
这就是客户需要创建的所有头文件了,当你使用 IDE 创建一个 MFC 程序时,他会自动为你创建上述头文件。另外还需要注意下面这 3 个头文件,它们在 MFC 类库中是真实存在的,并且名称相同。"afx.h" 声明了 CObject 基类,"afxwin.h" 声明了 MFC 中使用的大部分类的基类,"stdafx.h" 是为了减少重复编译设置的,用于建立一个预编译的头文件 .PCH 和一个预定义的类型文件 STDAFX.OBJ。由于MFC体系结构非常大,包含许多头文件,如果每次都编译的话比较费时,因此我们把常用的 MFC头 文件都放在 stdafx.h 中,如 afxwin.h、afxext.h、afxdisp.h、afxcmn.h 等,然后让 stdafx.cpp 包含这个 stdafx.h 文件。这样,由于编译器可以识别哪些文件已经编译过,所以stdafx.cpp就只编译一次,并生成所谓的预编译头文件。
//stdfx.h #include "afxwin.h" //其他必要的头文件
//#include <......>
//#include <......>
//#include <......> //链接必要的库
//#pragma comment(......)
//#pragma comment(......)
//#pragma comment(......)
//afxwin.h #pragma once
#include "afx.h" //CCmdTarget类声明
class CCmdTarget : public CObject
{
public:
CCmdTarget();
}; //CDocument类声明
class CDocument : public CCmdTarget
{
public:
CDocument();
}; //CWnd类声明
class CWnd : public CCmdTarget
{
public:
CWnd();
virtual BOOL Create();
BOOL CreateEx();
virtual BOOL PreCreateWindow();
}; //CFrameWnd类声明
class CFrameWnd : public CWnd
{
public:
CFrameWnd();
}; //CView类声明
class CView : public CWnd
{
public:
CView();
}; //CWinThread类声明
class CWinThread : public CCmdTarget
{
public:
CWnd* m_pMainWnd; CWinThread();
virtual BOOL InitInstance();
virtual int Run();
}; //CWinApp类声明
class CWinApp : public CWinThread
{
public:
CWinApp();
virtual BOOL InitApplication();
virtual BOOL InitInstance(); //覆盖
virtual int Run(); //覆盖
};
说明几点,CWnd 类中有创建窗口的虚函数,Create,CreateEx(前者的扩展版本),PreCreateWindow。 因此实际编写其派生类时需要覆盖这些函数。CWinApp 中有重要的 InitApplication() 函数和继承自 CWinThread 的 InitInstance() 和 Run(),编写其派生类通常要覆盖它们。值得一提的是,CWinThread 有一个数据成员 CWnd* m_pMainWnd,它是指向框架窗对象的指针。
//afx.h //演示需要,MFC实际上不包含<iostream>
#include <iostream>
using namespace std; //实际上下面这些重定义写在<WinDef.h>中,MFC编程时通常会自动包含该头文件,为演示方便就写这了
typedef int BOOL;
#define TRUE 1; //CObect类声明
class CObject
{
public:
CObject();
};
"afx.h" 中声明了祖宗类 CObject。
//MyDoc.cpp #include "stdafx.h"
#include "MyDoc.h" CMyDoc::CMyDoc()
{
cout<<"CMyDoc Constructor."<<endl;
}
//MyFrame.cpp #include "stdafx.h"
#include "MyFrame.h" //CMyFrame类方法定义
CMyFrame::CMyFrame()
{
cout<<"CMyFrame Constructor."<<endl;
}
//MyView.cpp #include "stdafx.h"
#include "MyView.h" //CMyView类方法定义
CMyView::CMyView()
{
cout<<"CMyView Constructor."<<endl;
}
//My.cpp #include "stdafx.h"
#include "My.h"
#include "MyFrame.h"
#include "MyDoc.h"
#include "MyView.h" //CMyWinApp类方法定义
CMyApp::CMyApp()
{
cout<<"CMyApp Constructor."<<endl;
}
BOOL CMyApp::InitInstance() //覆盖
{
cout<<"CMyApp::InitInstance()."<<endl; //下面的注释为 MFC 源码中的内容,使用RTTI实例化了CMyDoc、
//CMyFrame、CMyView,并且使用 CDocTemplate 类来连接管理
/*
// 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMyFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CMyView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
*/ this->m_pMainWnd = new CMyFrame();
return TRUE;
} //全局变量
CMyApp theApp; int main()
{
theApp.InitApplication();
theApp.InitInstance();
theApp.Run();
return ;
}
说明几点,InitInstance() 中的注释是 MFC 中的源码,我在这里贴出来是为了让大家理解 MFC 是在这个时候实例化了 CMyDoc、CMyFrame 和 CMyView,至于 RUNTIME_CLASS 是什么,下几章博客会详细介绍。
为什么 MFC 没有 WinMain()?
注意!最重要的东西来了!"My.cpp" 中有一个很关键的全局对象 CMyApp theApp,它是整个 MFC 初始化的关键。由于 C++ 中全局对象的构建将比程序进入点(DOS 环境为 main,Windows 为 WinMain)更早,因此 CMyApp 的构造函数最先被执行,完成一系列的初始化。注意到该演示程序里有 main() 函数,但是 MFC 程序中并没有(Window 程序主函数其实是 WinMain()),那么它去哪了呢?MFC 将 WinMain() 封装了起来,在 CMyApp theApp 实例化后会自动调用 WinMain() 函数并获得 theApp 对象的指针对其操作,让使用者看起来 theApp 才是程序的入口点。
然后是剩余的存在于MFC中 .cpp 文件:
//stdfx.cpp #include "stdafx.h"
//afx.cpp #include "afx.h" //CObject类声明
CObject::CObject()
{
cout<<"CObject Constructor."<<endl;
}
//afxwin.cpp #include "afxwin.h" //这里导入"CMyApp.h"是为了使用 theApp 全局变量以使用改造版的 AfxGetApp()
#include "My.h"
extern CMyApp theApp; //CCmdTarget类方法定义
CCmdTarget::CCmdTarget()
{
cout<<"CCmdTarget Constructor."<<endl;
} //CDocument类方法定义
CDocument::CDocument()
{
cout<<"CDocument Constructor."<<endl;
} //CWnd类方法定义
CWnd::CWnd()
{
cout<<"CWnd Constructor."<<endl;
}
BOOL CWnd::Create()
{
cout<<"CWnd::Create()."<<endl;
return TRUE;
}
BOOL CWnd::CreateEx()
{
cout<<"CWnd::CreateEx()."<<endl;
return TRUE;
}
BOOL CWnd::PreCreateWindow()
{
cout<<"CWnd::PreCreateWindow()."<<endl;
return TRUE;
} //CFrameWnd类方法定义
CFrameWnd::CFrameWnd()
{
cout<<"CFrameWnd Constructor."<<endl;
} //CView类方法定义
CView::CView()
{
cout<<"CView Constructor."<<endl;
} //CWinThread类方法定义
CWinThread::CWinThread()
{
cout<<"CWinThread Constructor."<<endl;
}
BOOL CWinThread::InitInstance()
{
cout<<"CWinThread::InitInstance()."<<endl;
return TRUE;
}
int CWinThread::Run()
{
cout<<"CWinThread::Run()."<<endl;
return ;
} //CWinApp类方法定义
CWinApp::CWinApp()
{
cout<<"CWinApp Constructor."<<endl;
}
BOOL CWinApp::InitInstance()
{
cout<<"CWinApp::InitInstance()."<<endl;
return TRUE;
}
BOOL CWinApp::InitApplication()
{
cout<<"CWinApp::InitApplication()."<<endl;
return TRUE;
}
int CWinApp::Run()
{
cout<<"CWinApp::Run()."<<endl;
return ;
}
值得注意的是 InitInstance() 虚函数是在 CWinThread 中被声明,InitApplication() 则是在 CWinApp中被声明。
CObject Constructor.
CCmdTarget Constructor.
CWinThread Constructor.
CWinApp Constructor.
CMyApp Constructor.
CWinApp::InitApplication().
CMyApp::InitInstance().
CObject Constructor.
CCmdTarget Constructor.
CWnd Constructor.
CFrameWnd Constructor.
CMyFrame Constructor.
CWinApp::Run().
代码我已经上传,编译一下就可以 MyMFC.zip
下一篇我会教大家怎么样写出一个好看的窗体
https://blog.csdn.net/raito__/article/details/51699828
如何优雅的写UI——(1)MFC六大核心机制-程序初始化的更多相关文章
- MFC六大核心机制之二:运行时类型识别(RTTI)
上一节讲的是MFC六大核心机制之一:MFC程序的初始化,本节继续讲解MFC六大核心机制之二:运行时类型识别(RTTI). typeid运算子 运行时类型识别(RTTI)即是程序执行过程中知道某个对象属 ...
- MFC六大核心机制之一:MFC程序的初始化
很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的MFC,很方便,不用学太多原理性的知识就可以做出各种窗口程序,但喜欢钻研的朋友肯定想知道,到底微软帮我们做了些什么,让我们在它的框架下 ...
- MFC六大核心机制
MFC六大核心机制概述 我们选择了C++,主要是因为它够艺术.够自由,使用它我们可以实现各种想法,而MFC将多种可灵活使用的功能封装起来,我们岂能忍受这种“黑盒”操作?于是研究分析MFC的核心机制成为 ...
- MFC六大核心机制之五、六:消息映射和命令传递
作为C++程序员,我们总是希望自己程序的所有代码都是自己写出来的,如果使用了其他的一些库,也总是千方百计想弄清楚其中的类和函数的原理,否则就会感觉不踏实.所以,我们对于在进行MFC视窗程序设计时经常要 ...
- MFC六大核心机制之三:动态创建
MFC中很多地方都使用了动态创建技术.动态创建就是在程序运行时创建指定类的对象.例如MFC的单文档程序中,文档模板类的对象就动态创建了框架窗口对象.文档对象和视图对象.动态创建技术对于希望了解MFC底 ...
- MFC六大核心机制之四:永久保存(串行化)
永久保存(串行化)是MFC的重要内容,可以用一句简明直白的话来形容其重要性:弄懂它以后,你就越来越像个程序员了! 如果我们的程序不需要永久保存,那几乎可以肯定是一个小玩儿.那怕我们的记事本.画图等小程 ...
- 如何优雅的写UI——(3)添加MFC选项卡
窗体创建完成,接下来我们讲讲控件的使用 首先在CFormView窗体下选项卡的成员变量,这里我选择MFC下的选项卡类库:CMFCTabCtrl class CtabView : public CFor ...
- 如何优雅的写UI——(2)MFC下基于CFormView的文档视图程序
在MFC中可以创建多种类型的窗口程序,如对话框程序.单文档结构程序(非文档/视图结构).单文档(文档/视图结构)以及多文档视图结构程序等. 在编写一般的小工具时,我们的首选显然是对话框程序,不过基于对 ...
- 如何优雅的写UI——(5)选项卡功能实现
先在我们的选项卡可以说能用了,每个标签页都能点进去,但是这还远远没到能用的地步,比如说你把窗口最大化后. 立马就露出马脚了,所以这篇我们要先讲讲tabctrl的最基本的功能实现 改变选项卡大小 上图的 ...
随机推荐
- Ext4.0 经常使用代码整理(一)
一:经常使用工具条上的定义 // 工具条 var toolbar = Ext.create("Ext.Toolbar", { items : [ yearC ...
- HDU 5303 Delicious Apples(贪心 + 背包 2015多校啊)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5303 Problem Description There are n apple trees plan ...
- HDU 5358 First One 数学+尺取法
多校的题,摆明了数学题,但是没想出来,蠢爆了,之前算了半天的s[i][j]的和,其实是积.其实比赛的时候我连log(s[i][j])+1是s[i][j]的位数都没看出来,说出来都丢人. 知道了这个之后 ...
- 洛谷P4051 [JSOI2007]字符加密
题目描述 喜欢钻研问题的JS 同学,最近又迷上了对加密方法的思考.一天,他突然想出了一种他认为是终极的加密办法:把需要加密的信息排成一圈,显然,它们有很多种不同的读法. 例如‘JSOI07’,可以读作 ...
- 突破极限 解决大硬盘上安装Sco Unix新思路
突破极限 解决大硬盘上安装Sco Unix新思路 [url]http://os.51cto.com/art/200809/89750.htm[/url] 硬盘越做越大,然我喜欢让我忧.10 ...
- WCF项目启动时错误处理
1. 原因:启动有wcf服务的项目时,报错,是因为wcf的服务没有启动. 解决办法:启动wcf的服务端口,127.0.0:4000,错误消失.
- 解决Visual Studio-IIS Express 支持局域网访问
- 请求由tomcat转到servlet的临界点
>>>>>>>>>>>>>>>>>>>>>>>>> ...
- 昼猫笔记 JavaScript -- 作用域技巧!!
简单理解 var zm = function (x) { var code = 'bb' return code }; 学过js的老哥们都知道,当这样简单的一个函数进入浏览器,浏览器开始解释代码,会将 ...
- power design设计数据库
power design是收费软件 大致设计流程: 画出概念数据模型,添加实体,连接实体间关系 生成物理数据模型,可以继续在此基础上修改 生成数据库脚本(一个.sql文件),文件中前面是删除表,后面是 ...