320 likes | 455 Views
第 3 章 MFC 应用程序框架. 3.1 早期的应用程序框架及其 MFC 类 3.2 最简单的 MFC 程序实例 3.3 应用程序的文档 / 视图结构 3.4 文档类 CDocument 的派生类 3.5 视图类 CView 的派生类 3.6 窗口框架类 CFrameWnd 的派生类 3.7 文档模板类 CDocTemplate 3.8 应用程序类的派生类 3.9 MFC 文档 / 视图应用程序框架中各个对象的关系 3.10 对象的动态组建. 本章主要内容:. MFC 的基本应用程序框架类 Windows 应用程序的文档/视图结构
E N D
第3章 MFC应用程序框架 3.1 早期的应用程序框架及其MFC类 3.2 最简单的MFC程序实例 3.3 应用程序的文档/视图结构 3.4 文档类CDocument的派生类 3.5 视图类CView的派生类 3.6 窗口框架类CFrameWnd的派生类 3.7 文档模板类CDocTemplate 3.8 应用程序类的派生类 3.9 MFC文档/视图应用程序框架中各个对象的关系 3.10 对象的动态组建
本章主要内容: • MFC的基本应用程序框架类 • Windows应用程序的文档/视图结构 • 文挡/视图结构的应用程序框架类 • 对象的动态创建
3.1 早期的应用程序框架及其MFC类 1. 早期的应用程序框架 与例2-3非常相似
CObject CCmdTarget CWnd CFrameWnd 3.1 早期的应用程序框架及其MFC类 2. MFC的窗口类——CFrameWnd • MFC中相当一部分类的基类 • 程序诊断功能 • 对象的动态创建、序列化功能 • 消息处理能力 • 提供窗口 • 消息处理能力 • 应用程序主窗口 • 其他子窗口的容器
3.1 早期的应用程序框架及其MFC类 2. MFC的窗口类——CFrameWnd
CObject CCmdTarget CWinThread CWinApp 3.1 早期的应用程序框架及其MFC类 3. CWinApp类 支持线程管理 WinMain函数的函数体的封装 应用程序类在类族中的位置
3.1 早期的应用程序框架及其MFC类 3. CWinApp类
3.2 最简单的MFC程序实例 Type of New Project: MFC App Wizard[exe] WinMain -> AfxWinMain 应用程序初始化 消息循环
3.2 最简单的MFC程序实例 MFC如何使用应用程序对象 MFC应用程序代码没有包含自己定义的类以外的任何可执行代码。例如,它没有main或WinMain函数。在整个程序中唯一的语句就是具有全局有效性的用来实例化应用程序对象的语句。那么到底是什么启动了程序的运行,应用程序对象又是何时起作用的呢? 揭开这个谜底的最好方法就是看看主框架的源代码,一个MFC提供的源代码文件(winmain.cpp)找那个包含了一盒AfxWinMain函数,它在MFC中的作用相当于WinMain(实际上,当您购买了Visual C++,同时也就得到了MFC提供的源代码。)AfxWinMain广泛使用应用程序对象,这就是为什么应用程序对象必须全局声明的原因。全局变量和对象在任何其他代码执行以前被创建,在AfxWinMain运行以前,应用程序对象必须在内存中存在。 运行一开始,AfxWinMain就调用AfxWinInit函数来初始化主框架,并将hInstance、nCmdShow以及其他AfxWinMain函数参数复制给应用程序对象的数据成员,然后它调用InitApplication和InitInstance。然后,AfxWinMain调用应用程序对象的Run函数,该函数执行消息循环。
3.3 应用程序的文档/视图结构 1. 基本概念 应用程序主窗口 CFrameWnd 窗口框架类 CView 视图类 CDocument 文档类 • 原因:应用程序主窗口需要管理, • 窗口本身的一些事务(max、min、close、menu command response,etc) • 程序的数据 • 数据的显示和接受用户区的消息与处理 • 因此,MFC把早期的窗口类的功能分解成三个部分: • 数据存储、管理部分(CDocument) • 数据显示与用户交互部分(CView) • 管理窗口框的大小、标题、菜单条、状态条的窗框部分(CFrameWnd)
3.3 应用程序的文档/视图结构 1. 基本概念 由文档模板类CDocTemplate进行统一创建和管理
SDI (Single Document Interface, 单文档界面) MDI (multi-Document Interface, 多文档界面) 3.3 应用程序的文档/视图结构 2. SDI & MDI
3.4 文档类CDocument的派生类 应用程序的文档类由CDocument派生 序列化/串行化 Serialization • Serialize()函数: • 文件操作时响应 • Read data from file • Write data to file 理解P60:例3-2
3.5 视图类CView的派生类 为应用程序框架提供用户区,承担用户数据的显示(表现形式,图形、文字等),同时替代窗口框架接收外界的输入。因此,该类是程序员编码最多的地方。 派生关系 与文档类的联系纽带 用户区的绘制,注意:谁来触发 1)自动重绘; 2)手动视图更新 理解P62:例3-3
3.5 视图类CView的派生类 OnDraw(CDC * pDC) 画布 Canvas OnDraw 窗口重绘 指令(消息) 绘制 CDC Class of Context Device 设备环境类 画笔、画刷、调色板等
CFrameWnd 派生 CMainFrame 3.6 窗口框架类CFrameWnd的派生类 当使用MFC AppWizard创建应用程序时 C:\Program Files\Microsoft Visual Studio\VC98\MFC\Include\AfxWin.h
CObject CCmdTarget CDocTemplate CSingleDocTemplate CMultiDocTemplate 3.7 文档模板类CDocTemplate 单一的文档指针
3.7 文档模板类CDocTemplate 文档类链表 查阅CPtrList类
3.8 应用程序类的派生类 RUNTIME_CLASS分别创建了doc、view和窗口框架类的对象
3.8 应用程序类的派生类 • 程序员的主要工作-重要的类及其函数成员 • CWinApp的InitInstance的重新定义 • CDocument中程序数据的管理 • 在CView中获取CDocument的指针,并能访问程序数据 • CView的OnDraw函数编写数据的显示代码 • 实现各个必要的消息映射表
系统 窗口框架对象 应用程序对象 视图对象 文档模板 文档对象 3.9 MFC文档/视图应用程序框架中各个对象的关系 1)应用程序各对象的创建顺序 CWinApp theApp; theApp.InitInstance
3.9 MFC文档/视图应用程序框架中各个对象的关系 2)应用程序各对象之间 (自学)
3.10 对象的动态创建 1) 问题的提出和解决 WHY? 程序运行时,动态地从已知(已定义)的类中创建某一类的对象。创建时类的类型(不同的文档等)、个数是不确定的(动态地)。 HOW? 1)对象的创建离不开其构造函数。所以只有正确地调用类的构造函数,便能正确地创建该类的对象。 2)建立正确的类名及其构造函数的对应关系。借助这种对应关系表,便可以在传递类名时,创建该类的对象。 如,CObject * CreateObject() { return new CMyWnd; }
CMyDoc m_pfnCreateObject CMyWnd m_pfnCreateObject …… CMyView m_pfnCreateObject 3.10 对象的动态创建 关系维护 CMyDoc 类名 CMyWnd 函数指针 CreateObject() return new CMyDoc; 类名 CreateObject() 函数指针 CMyView return new CMyWnd; 类名 函数指针 CreateObject() return new CMyView; 类与对象创建函数映射表
链接类信息表 3.10 对象的动态创建 链接类族信息 2) 类信息表及其声明和实现 类信息表:用于动态创建类对象所需的信息。CRuntimeClass结构体。
class1 class2 classN classN 类的信息 类的信息 类的信息 类的信息 …… m_pNextClass m_pNextClass m_pNextClass m_pNextClass NULL 类信息链表的结构 3.10 对象的动态创建 2) 类信息表及其声明和实现 相关的宏 class A : public B { public: DECLARE_DYNCREATE(A) … }; IMPLEMENT_DYNCREATE(A, B) m_pBaseClass
连接符 续行符 3.10 对象的动态创建 3) 对象类信息表的提取 RUNTIME_CLASS宏 #define RUNTIME_CLASS(class_name) \ ((CRuntimeClass *)(&class_name::class##class_name)) Remarks Use this macro to get the run-time class structure from the name of a C++ class. RUNTIME_CLASS returns a pointer to a CRuntimeClass structure for the class specified by class_name. Only CObject-derived classes declared with DECLARE_DYNAMIC, DECLARE_DYNCREATE, or DECLARE_SERIAL will return pointers to a CRuntimeClass structure. Example // example for RUNTIME_CLASS CRuntimeClass* ptr = RUNTIME_CLASS( CAge ); ASSERT( lstrcmp( ptr->m_lpszClassName, "CAge" ) == 0 );
进一步理解RUNTIME_CLASS DYNAMIC支持 在CObject 派生类中,可以获得动态"验证"支持,访问运行时类信息 方法: 声明时添加宏:DECLARE_DYNAMIC( class_name ) 实现时添加宏: IMPLEMENT_DYNAMIC DECLARE_DYNAMIC(class_name)相当于在类中添加如下声明 : protected: static CRuntimeClass* PASCAL _GetBaseClass(); public: //静态成员CRuntimeClass,给此派生类添加了运行时类信息, //这样就可以使用CRuntimeClass成员判断类信息了。 //此成员名字格式为"class"+"类名",RUNTIME_CLASS()宏就是返回此结构的指针 static const AFX_DATA CRuntimeClass class##class_name; virtual CRuntimeClass* GetRuntimeClass() const;
进一步理解RUNTIME_CLASS IMPLEMENT_DYNAMIC: #define IMPLEMENT_DYNAMIC(class_name, base_class_name) \ IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL) #define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \ //返回基类运行时信息结构的指针 CRuntimeClass* PASCAL class_name::_GetBaseClass() \ { return RUNTIME_CLASS(base_class_name); } \ //初始化本类的运行时信息,依次为类名、大小,版本 ,NULL,基类 AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \ #class_name, sizeof(class class_name), wSchema, pfnNew, \ &class_name::_GetBaseClass, NULL }; \ //返回运行时类信息,重载了CObject的GetRuntimeClass, //使得CObject中声明的接口对具体的派生类有效 CRuntimeClass* class_name::GetRuntimeClass() const \ { return RUNTIME_CLASS(class_name); } \
类的声明 进一步理解RUNTIME_CLASS DYNCREATE支持 类的实例动态生成支持 方法: 添加声明:DECLARE_DYNCREATE( class_name ) 添加实现:IMPLEMENT_DYNCREATE( class_name, base_class_name ) 类的实现 #define DECLARE_DYNCREATE(class_name) \ //具有DYNAMIC支持 DECLARE_DYNAMIC(class_name) \ //对象建立支持 static CObject* PASCAL CreateObject();
进一步理解RUNTIME_CLASS IMPLEMENT_DYNCREATE(class_name, base_class_name): #define IMPLEMENT_DYNCREATE(class_name, base_class_name) \ //动态建立对象 CObject* PASCAL class_name::CreateObject() \ { return new class_name; } \ //填写运行时类信息,与DYNAMIC不同的是,有pfnNew参数 IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, class_name::CreateObject)
3.10 对象的动态创建 3) 对象类信息表的提取