1.53k likes | 1.81k Views
第三章 MFC 程序设计. 本章的内容,简洁而不失完整性:由应用程序向导建立程序框架,由资源编辑器建立用户界面,由类向导建立成员变量、消息映射和处理函数,加上文档序列化和注册 MFC 包含了所有与系统相关的类 ( 包括图形用户界面相关的类 ) ,其中封装了大多数的 API 函数,提供了应用程序框架和开发应用程序的工具如应用程序向导、类向导、可视化资源设计等高效工具,用消息映射处理消息响应,大大简化了 Windows 应用程序的开发工作,使程序员可以从繁重的编程工作中解脱,提高工作效率。. 目录. 3.1. MFC 程序设计简介 3.2. MFC 程序设计
E N D
第三章 MFC程序设计 本章的内容,简洁而不失完整性:由应用程序向导建立程序框架,由资源编辑器建立用户界面,由类向导建立成员变量、消息映射和处理函数,加上文档序列化和注册 MFC包含了所有与系统相关的类(包括图形用户界面相关的类),其中封装了大多数的API函数,提供了应用程序框架和开发应用程序的工具如应用程序向导、类向导、可视化资源设计等高效工具,用消息映射处理消息响应,大大简化了Windows应用程序的开发工作,使程序员可以从繁重的编程工作中解脱,提高工作效率。
目录 3.1.MFC程序设计简介 3.2.MFC 程序设计 3.3.用户界面资源的应用 3.4.对话框的应用 3.5.基于对话框的应用程序设计实例 3.6.文本和图形输出 3.7.文档的保存 3.8. MDI应用程序设计
3.1 MFC程序设计简介 类库 MFC类库 基础类 (已在第二章中介绍) 宏、全局变量及全局函数 应用程序框架 3.1.1 MFC定义的宏、全局函数及全局变量 1. MFC提供的主要宏: 消息映射宏:声明消息映射表宏DECLARE_MESSAGE_MAP、消息映射定义宏BEGIN_MESSAGE_MAP和END_MESSAGE_MAP消息映射表入口宏ON_消息名。(本章只介绍消息映射宏)
3.1.1 MFC定义的宏、全局函数及全局变量 动态MFC对象宏 DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC 动态创建对象宏 DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE 运行时类宏: RUNTIME_CLASS 序列化宏: DECLARE_SERIAL和IMPLEMENT_SERIAL 诊断服务宏: ASSERT、VERIFY 跟踪服务宏: TRACE 异常处理宏: THROW
3.1.1 MFC定义的宏、全局函数及全局变量 2. 全局函数 MFC提供一些不属于任何类的全局函数,函数名以Afx开头,可以被应用程序中的所有类和函数所调用。如:AfxAbort(),无条件终止一个应用程序的执行;AfxGetAppName(),返回指向应用程序名的字符串指针;AfxWinInit(),由WinMain()调用,对MFC应用程序进行图形用户界面(GUI)的初始化等等。 3. 全局变量 全局变量名以afx开头,如afxTraceFlag、afxDump等,主要与异常处理有关。
3.1.2 MFC命名规则 匈牙利命名法规定: • 变量名以一个或多个小写字母开头作为前缀,标识变量的数据类型; • 类型标识后是若干个单词,标识变量含义,每个单词以大写字母开头,其余字母小写; • MFC中所有类及自定义类都以C开头; • 类成员变量以m_为前缀,下划线后是变量名等等。
前缀 数据类型 例 B,b BOOL bIsValid Ch char ChText,ChGrade c class cString Cs CString CsName Dw DWORD DwFlags fn function fnSort handle hWnd,hPen i int iCount p pointer pFrame sz ASCII string szName m_ Data member m_szName 表3.1 命名规则 h
3.1.3 应用程序框架介绍 MFC提供了构造Windows应用程序的框架,它不仅为应用程序建立标准的结构,生成一系列起动文件,还提供标准的图形用户界面如菜单、工具栏、状态条等供开发人员在程序中补充完善,开发人员只需要完成针对特定应用的代码。使用应用程序框架的应用程序代码小,运行速度快,它提供的工具也降低了编码的难度。
1.文档-视图结构 文档视图结构提供一种连续处理数据的方式,使应用程序的数据与数据的显示分开处理。 程序的数据储存在文档类中(作为文档类的数据成员),是对数据的抽象表示。数据显示由视图负责,视图是程序窗口的客户区,框架窗口是客户区的框架,程序数据显示在窗口,用户通过视图与程序交互。 一个文档可以对应多个不同视图;而一个视图一般只对应一个文档。当文档数据发生变化时,视图显示的状态也会随之变化;对于多个视图的情况,基于同一文档的多个视图的显示同步变化。
应用程序对象 发送至框架窗口的消息 发送至视图对象的消息 框架窗口对象 文档与视图之间的双向数据交换 视图对象 文档对象 (保存程序数据) 文档、视图及框架的关系: 图3.1
2. 单文档界面应用 单文档界面应用程序简称SDI(Single Document Interface),只允许一个框架窗口存在,每次只能创建和拥有一个文档对象,也只允许打开一个文档。 SDI各类关系图 可将文档比作一个文件袋,专门用于保存文件数据,需要看时需从文件袋(文档)中取出来,放到桌面(视图)上看。
应用程序类 文档类 (保存数据) 框架类( 窗口框架) 视图类 (显示数据并 与用户交互) 资源(决定用户 界面的显示) 单文档模板 返回
文档类(保存数据) m_pDocTemplate m_viewList 单文档模板 CRuntimeClass*m_pDocClass CRuntimeClass*m_pFrameClass CRuntimeClass*m_pViewClass UINT m_nIDResource 框架类( 窗口框架) m_pDocument 视图类(显示数据 并与用户交互) 资源(决定用户 界面的显示) 应用程序类 CDocManager* m_pDocManager 返回
3.多文档界面应用 多文档界面应用简称MDI(Multiple Document Interface),允许有多个文档类、视图类和多个文档模板CMultiDocTemplate(建立多种文档-视图-子框架关系)存在,运行时允许打开多个MDI子框架窗口,拥有各自的文档和视图对象。示例关系图 在打开的多个子窗口中,只有一个活动窗口,应用程序菜单和工具栏的所有的命令都是针对活动窗口的。
文档类2 文档类1 子框架 子框架 视图类2 视图类1 资源2 资源1 文档模板2 文档模板1 主 框 架 … 其它文档模板 CDocManager 应用程序类 CDocManager* m_pDocManager 返回
4. 非文档/视图结构应用 文档/视图结构可以充分发挥应用程序框架和MFC的优势,是MFC应用程序的核心,但使用它也要付出运行效率和代码增大的代价。 文档/视图结构对应用程序来说并不是必不可少的,有两种常见的非文档/视图结构应用。
常见非文档/视图结构应用: (1).基于对话框的应用,它包含一个CWinApp对象和一个模态对话框,对话框负责数据的存储和显示 。关系图 (2).SDI或MDI应用,都可以不包含文档对象,SDI包含一个CWinApp对象,一个框架窗口对象和一个从CWnd派生的CChildView视图对象,视图对象负责数据的显示,MDI应用还包括一个子框架窗口。
对话框类 作为应用程序主窗口 负责保存和显示数据 应用程序类 Dialog-based Application 返回
主框架,应用程序主窗口 CChildView视图类 负责保存和显示数据 应用程序类 返回 SDI Application, None Doc/View
主框架,应用程序主框架窗口 子框架, 视图框架窗口 子框架, 视图框架窗口 CChildView 视图类 … CChildView 视图类 应用程序类 返回 MDI Application, None Doc/View
5. MFC应用程序创建 应用程序向导 集成开发环境 资源编辑器 Create .def .cpp .h .dsw .dsp .ico .rc .bmp 源文件 资源文件 .obj .res Build 编译 资源编译 类向导 应用程序 图3.2 MFC应用程序开发过程
6.最简单的MFC应用程序示例 对于一个MFC应用程序来说,只有CWinApp的派生对象是必不可少的,其它类均可视情况进行取舍。下例是一个最简单的MFC应用程序。 例:显示一个消息框。 (1).创建工程,工程类型选择Win32 Application,工程名为Ex3_0; (2).创建C++源文件,文件名Ex3_0.cpp,内容如下:
#include <afxwin.h> class CEx3_0App: public CWinApp{ //CWinApp派生类 public: virtual BOOL InitInstance() { MessageBox(NULL,”SimplistMFC Application.”, ”Ex3_0”,MB_OK); return TRUE; } }; CEx3_0App theApp; //全局变量,代表应用程序本身 CWinApp::InitInstance()虚函数完成应用程序的初始化工作,派生的应用程序类都要重载此函数进行初始化,如完成主框架窗口的构造,窗口定义显示等工作。
(3).设置使用MFC。 选择Project|Settings…菜单项,选择General页面,在Microsoft Foundation Classes组合框中选择Use MFC in a shared DLL。 (4).编译生成并运行此工程,结果显示一个消息框,如下图: 图3.3 最简单MFC应用示例
3.2.MFC 程序设计 MFC应用程序的执行过程(参见图3.4): 1.构造全局对象—CWinApp派生类对象; 2.运行由应用程序框架提供的WinMain函数;
3.在WinMain中,通过afxGetApp()获得全局对象的指针pApp,调用全局函数AfxWinInit(),为CWinApp的成员3.在WinMain中,通过afxGetApp()获得全局对象的指针pApp,调用全局函数AfxWinInit(),为CWinApp的成员 变量m_hInstance, m_hPrevInstance, m_lpCmdLine, m_nCmdShow赋初值; 然后调用pApp->InitApplication(),这是CWinApp的虚函数,一般不需要改写; 调用 pApp->InitInstance(),每个程序都必需改写这个函数,进行应用程序初始化; 4.在InitInstance()函数中,先用new构造一个CFrameWnd派生类对象,其构造函数又调用Create(),创建主窗口,MFC依此自动为应用程序注册窗口类; 调用ShowWindow()显示窗口,调用UpdateWindow(),发出WM_PAINT消息;
5.回到WinMain中,调用pApp->Run,进入消息循环,通过API函数GetMessage()获得消息,TranslateMessage()处理消息,DispatchMessage()派送消息到CWnd::DefWindowProc(),DefWindowProc()按照消息映射表的定义将消息分发到各相应消息处理函数;5.回到WinMain中,调用pApp->Run,进入消息循环,通过API函数GetMessage()获得消息,TranslateMessage()处理消息,DispatchMessage()派送消息到CWnd::DefWindowProc(),DefWindowProc()按照消息映射表的定义将消息分发到各相应消息处理函数; 如消息队列为空,则Run调用CWinApp::OnIdle()进行空闲处理,重载OnIdle()可以去处理后台程序(低优先级); 6.若用户选择菜单File/Close,则程序收到WM_COLSE消息,调用::DestroyWindow()发出WM_DESTROY消息,然后调PostQuitMessage(),发出WM_QUIT消息,此时Run会结束其内部消息循环,调用ExitInstance(); 7.最后,返回WinMain(),执行WinTerm(),结束程序运行。
几点解释: (1).每个MFC应用都有stdafx.h头文件,由应用程序向导自动生成; (2).每个MFC应用都应包含头文件afxwin.h,其内部又包含了其他头文件。 (3).每一个MFC应用程序都包含唯一的CWinApp派生类对象,代表应用程序本身。 (4).MFC应用程序中看不到WinMain()入口函数,它被 封装在应用程序框架里,运行时自动调用;
3.2.2.MFC消息映射 消息映射(Message Map)机制: 消息映射机制规定每个要响应消息的类定义自己的消息映射表,将消息与处理消息的函数联系在一起;当事件发生产生消息时,在程序中各个类的消息映射表中查找,当找到消息处理函数时,便将消息送至对应的消息处理函数进行处理。每个从CCmdTarget所派生的类都可以接收消息,都可以定义自己的消息映射表Message Map。
消息映射表: 1.消息 消息是Windows用来通知MFC应用程序事件发生的主要方式,应用程序的行为取决于对消息的响应方式。 MFC把消息分为三大类: (1).标准Windows消息(WM_XXX), (2).命令消息(WM_COMMAND), (3).控件通知消息。
2.消息映射 消息映射将消息与应用程序提供的处理该消息的成员函数联系在一起,一组消息映射构成消息映射表。 消息映射表由BEGIN_MESSAGE_MAP和END_MESSAGE_MAP这一对宏作开始和结束,中间是若干消息映射入口 每一个入口由特定的消息宏ON_XXX组成,将一个消息与与之对应的处理函数联系在一起。
定义和使用消息映射表的步骤: ① 在头文件的类声明中添加DECLARE_MESSAGE_MAP宏声明消息映射;例如 ② 在类的定义文件(.cpp)定义消息映射表,方法是在 文件的开始处添加BEIGN_MESSAGE_MAP和END_MESSAGE_MAP这一对宏,在它们之间放置消息映射宏,定义并初始化消息映射表,在消息与处理消息的函数之间建立联系;例如 ③ 在类的定义文件(.cpp)中添加消息处理函数。例如
宏名 消息 默认处理函数 消息宏: (1).对标准Windows消息的响应 ON_WM_CHAR WM_CHAR OnChar ON_WM_ CLOSE WM_ CLOSE OnClose ON_WM_CREATE WM_ CREATE OnCreate ON_WM_LBUTTONDOWN WM_ LBUTTONDOWN OnLButtonDown ON_WM_LBUTTONUP WM_ LBUTTONUP OnLButtonUp ON_ WM_MOUSEMOVE WM_ MOUSEMOVE OnMouseMove 消息宏ON_WM_XXX定义对标准Windows消息WM_XXX的响应,所有派生自CWnd的类均可响应这类消息,系统默认的响应函数为OnXxx,对应关系见下面的表3.3: 由于有系统默认的消息和处理函数,在消息映射表中只写宏名,如: ON_WM_CREATE()
(2).对命令消息的的响应 。 ON_COMMAND宏定义对命令消息的响应,格式为 ON_COMMAND(命令ID,响应函数) 举例如下: ON_COMMAND ( IDM_ABOUT, OnAbout ) //表示IDM_ABOUT消息由OnAbout()响应 ON_COMMAND ( IDM_FILENEW, OnFileNew) ON_COMMAND ( IDM_FILEOPEN, OnFileOpen) 所有由用户定义的命令消息也由ON_COMMAND定义 消息映射关系。
(3).对控件消息的响应 控件消息由按钮(BN_)、编辑框(EN_)、组合框(CBN_)、列表框(LBN_)等产生,在消息名前加上ON_即构成宏名, 举例如下: ON_BN_CLICKED (按钮ID,响应函数) ON_CBN_DBCLK (组合框ID,响应函数) ON_EN_SETFOCUS (组合框ID,响应函数) ON_LBN_DBCLK (列表框ID,响应函数) 分别表示选择各个控件后,产生的消息由其后面定义 的函数进行处理。
3.消息处理 消息一般由应用程序中对象的成员函数来响应。当某个事件发生后,系统向应用程序消息队列放入一个消息,CWinApp对象取得消息并按照消息映射表的定义将消息派送到某个类的相应的消息处理函数进行处理。 使用ClassWizard(见第五章菜单中View菜单介绍)定义消息映射关系,添加消息处理函数,系统会自动生成函数原型和函数框架,开发人员只要添加函数体代码,可以避免一些常见错误的产生。
程序中类响应windows消息的优先顺序: 取决于消息传递的顺序。 对于一般的Windows消息,由产生这些消息的对象响应,如窗口的创建WM_CREATE,窗口的移动WM_MOVE,由产生消息的窗口类响应,如果找不到相应的处理函数,则将消息传递到其父类,一般情况都有缺省的响应函数; 对于命令消息WM_COMMAND,可由拥有菜单资源的所有类来处理,传递的顺序是视图类、文档类、文档模板类、框架类、应用程序类,即这些类处理命令消息的优先顺序。 详见第2章。
将CMyView的基类CView也包含进去,当在派生类中没找到处理函数时,允许在基类中继续查找。将CMyView的基类CView也包含进去,当在派生类中没找到处理函数时,允许在基类中继续查找。 视图类的定义文件(.cpp)中必须有消息映射定义: BEIGN_MESSAGE_MAP(CMyView,CView) //消息映射表定义 ON_WM_LBUTTONDOWN() //函数名为OnLButtonDown … //其它消息映射入口 END_MESSAGE_MAP() 以及对应的消息响应函数定义: void CMyView::OnLButtonDown(UINT nFlags, CPoint point){ … //处理消息的操作代码,程序中具体实现 } 返回 注
例如,当用户在视图窗口中按下鼠标左键时,Windows发送WM_LBUTTONDOWN消息到应用程序,假设视图类需要响应这些消息,在类的头文件(.h)中有函数声明和消息映射表的声明:例如,当用户在视图窗口中按下鼠标左键时,Windows发送WM_LBUTTONDOWN消息到应用程序,假设视图类需要响应这些消息,在类的头文件(.h)中有函数声明和消息映射表的声明: Class CMyView: public CView { … afx_msg void OnLButtonDown(UINT nFlags,CPoint point); //函数原型声明 DECLARE_MESSAGE_MAP() //消息映射表声明 … }返回 afx_msg标识消息映射函数,所有由ClassWizard生成的消息处理函数原型说明前都有这一标识。 注
3.2.3.文档/视图结构 1.文档类 在基于文档/视图结构的应用程序中,CDocument的派生对象负责读入、保存和管理应用程序的数据。每个文档类可能与多个视图类关联,而每个视图包含一个指向与其相关的文档的指针。 文档类允许向其中增加数据成员,应用程序的数据可以是任何类型的,或者是任何自定义的数据结构,如结构体,类或链表等。
3.2.3.文档/视图结构 文档类的常用成员函数(带√的表示是可重定义函数) 表3.4
2.视图类 视图类对象代表了应用程序的窗口客户区,是文档信息的一个显示窗口(视口),它同时也是用户与程序之间信息交互的桥梁,用户对数据的编辑和修改需要通过键盘或鼠标进行,这些消息都由视图类接受后再反映到文档类。
CWnd CView CCtrlView CListView CEditView CRichEditView CTreeView CScrollView CFormView CDaoRecordView CDaoRecordView 视图类的派生关系: 图3.5
CView类提供了向视图或打印机进行输出的基本框架,决定了视图的基本特性,派生的视图类可以重载基类的函数,或增加新的函数以决定特定视图的特性。CView类提供了向视图或打印机进行输出的基本框架,决定了视图的基本特性,派生的视图类可以重载基类的函数,或增加新的函数以决定特定视图的特性。 CView的派生类可以多种方式显示文档类的信息,每个视图类根据显示的方式各自定义其显示代码。 MFC提供了一系列这样的类,如 CTreeView 树型视图 CListView 列表型视图 CFormView 显示基于对话框模板的视图CScrollView 带滚动条的视图。
1)视图类实现特定视图的特性的虚函数 表3.5
2) 视图类的成员函数访问与其关联的文档类的方式 CEx3_1Doc* pDoc = GetDocument(); 通过GetDocument获得与视图关联的文档类指针,通过指针pDoc访问文档类的数据成员和函数成员,在视图(窗口)中显示文档对象,当用户通过视图改变文档数据时,可设置自动更新文档内容。 文档/视图结构已经把应用程序所需要的数据处理与显示(虚函数)的框架构造好,程序设计人员只需要将具体应用的数据添加到文档类,并根据数据类型在视图类的虚函数OnDraw()中完成数据的显示。
3.文档模板类 CDocTemplate类将框架、视图、文档对象及应用 程序的资源捆绑在一起。应用程序每打开一个文件,至少创建一个文档对象、一个视图对象和一个框架窗口,而文档模板对象负责管理所有这些对象,并将各种相关资源与之联系在一起。 在大多数情况下,程序不需要对这个类进行修改。 对于SDI应用,使用CSingleDocTemplate文档模板。 对于MDI应用,使用CMultiDocTemplate文档模板。MDI允许有多个文档模板,将不同文档-视图类联系在一起,实现不同文档视图显示关系。