第3章 MFC编程.docx
- 文档编号:17964939
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:75
- 大小:553.81KB
第3章 MFC编程.docx
《第3章 MFC编程.docx》由会员分享,可在线阅读,更多相关《第3章 MFC编程.docx(75页珍藏版)》请在冰点文库上搜索。
第3章MFC编程
第3章MFC编程
3.1MFC简介
3.1.1MFC是什么
MFC的英文全称是MicrosoftFoundationClass,即微软的基础类库。
类库是一个在应用程序中使用的相互关联的C++类的集合。
MFC的本质就是一个包含了许多已经定义好的对象的类库。
MFC中的各种类结合起来构成了一个应用程序框架,它的目的就是让程序员在此基础上来建立Windows下的应用程序。
MFC框架定义了应用程序的轮廓,并提供了用户接口的标准实现方法,程序员所要做的就是通过预定义的接口把具体应用程序特有的东西填入这个轮廓。
很多公司都提供C++类库,除了Microsoft公司的MFC,常用的类库还有Borland公司的OWL(objectwindowslibrary)和IBM公司OCL(openclasslibrary)。
大家知道,虽然要编写的程序在功能上千差万别的,但从本质上来讲,都可以化分为用户界面设计、文件操作、多媒体使用、数据库访问等一些最主要的方面。
这一点正是微软提供MFC类库最重要的原因,在这个类库中包含了一百多个程序开发过程中最常用到的对象。
在进行程序设计的时候,如果类库中的某个对象能完成所需要的功能,这时只要简单地调用已有对象的方法就可以了。
同时还可以利用面向对象技术中很重要的“继承”方法从类库中的已有对象派生出程序员自己的对象,这时派生出来的对象除了具有类库中的对象的特性和功能之外,还可以由程序员根据需要加上所需要的特性和方法,产生一个更专门的,功能更强大的对象。
当然,也可以在程序中创建全新的对象,并根据需要不断完善对象的功能。
MFC是VisualC++软件的一部分。
MFC库中的所有类形成了建立应用程序的框架,利用这些类可以充分支持Windows应用程序开发。
构建MFC库的主要目的是为方便Windows应用程序的开发和设计。
正是由于MFC编程方法充分利用了面向对象技术的优点,它使得编程时极少需要关心对象方法的实现细节,同时类库中的各种对象的强大功能足以完成设计任务的绝大部分,这使得应用程序设计中,程序员所需要编写的代码大为减少,有力地保证了程序的良好的可调试性。
MFC是一个框架(framework)式类库,框架式类库同一般类库的不同之处在于,库中的各个类之间是有联系的,它们是按照框架所定义的模式去协作完成任务的。
所以,要学习MFC,首先就要了解各个类之间是如何协作的以及它们的接口。
最后要指出的是MFC类库提供的对象的各种属性和方法都已经过谨慎的编写和严格的测试,可靠性很高,这就保证了使用MFC类库不会影响应用程序的可靠性和正确性。
3.1.2MFC的特点
●1.封装
构成MFC框架的是MFC类库。
MFC类库是C++类库。
这些类或者封装了Win32应用程序编程接口,或者封装了应用程序的概念,或者封装了OLE特性,或者封装了ODBC和DAO数据访问的功能等,分述如下:
(1)对Win32应用程序编程接口封装
用一个C++Object来包装一个WindowsObject。
例如:
ClassCWnd是一个C++windowsobject,它把Windowswindow(HWND)和与Windowswindow有关的API函数封装在C++windowobject的成员函数内,后者的成员变量m_hWnd就是前者的窗口句柄。
(2)对应用程序概念的封装
使用API编写Windows应用程序时,总要定义窗口过程,登记WindowsClass,创建窗口等。
MFC把许多类似的处理封装起来,替程序完成这些工作。
另外,MFC提出了以文档视图结构为中心的编程模式,MFC类库封装了对它的支持。
文档是用户操作的数据对象,视图是数据操作的窗口,用户通过它处理、查看数据。
(3)对COM/OLE特性的封装
OLE建立在COM模型之上,由于支持OLE的应用程序必须实现一系列的接口(interface),因而相当繁琐。
MFC的OLE类封装了OLEAPI大量的复杂工作,这些类提供了实现OLE的更高级接口。
(4)对ODBC功能的封装
以少量的能提供与ODBC之间更高级接口的C++类,封装了ODBCAPI的大量的复杂工作,提供了一种数据库编程模式。
●2.继承
首先,MFC抽象出众多类的共同特性,设计出一些基类作为实现其他类的基础。
这些类中,最重要的类是CObject和CCmdTarget。
CObject是MFC的根类,绝大多数MFC类是其派生的,包括CCmdTarget。
CObject实现了一些重要的特性,包括动态类信息、动态创建、对象序列化、对程序调试的支持等。
所有从CObject派生的类都将具备或者可以具备CObject所拥有的特性。
CCmdTarget通过封装一些属性和方法,提供了消息处理的架构。
MFC中,任何可以处理消息的类都从CCmdTarget派生。
针对每种不同的对象,MFC都设计了一组类对这些对象进行封装,每一组类都有一个基类,从基类派生出众多更具体的类。
这些对象包括以下种类:
窗口对象,基类是CWnd;应用程序对象,其类是CWinThread;文档对象,基类是CDocument等。
程序员将结合自己的实际,从适当的MFC类中派生出自己的类,实现特定的功能,达到自己的编程目的。
●3.虚函数和动态约束
MFC以“C++”为基础,自然支持虚函数和动态约束。
但是作为一个编程框架,有一个问题必须解决;如果仅通过虚函数来支持动态约束,必然导致虚函数表过于臃肿,消耗内存,效率低下。
例如,CWnd封装Windows窗口对象时,每一条Windows消息对应一个成员函数,这些成员函数为派生类所继承。
如果这些函数都设计成虚函数,由于数量太多,实现起来不现实。
于是,MFC建立了消息映射机制,以一种富有效率、便于使用的手段解决消息处理函数的动态约束问题。
这样,通过虚函数和消息映射,MFC类提供了丰富的编程接口。
程序员继承基类的同时,把自己实现的虚函数和消息处理函数嵌入MFC的编程框架。
MFC编程框架将在适当的时候,适当的地方来调用程序的代码。
●4.MFC的宏观框架体系
如前所述,MFC实现了对应用程序概念的封装,把类,类的继承,动态约束,类的关系和相互作用等封装起来。
这样封装的结果对程序员来说,是一套开发模板(或者说模式)。
针对不同的应用和目的,程序员采用不同的模板。
例如,SDI应用程序的模板,MDI应用程序的模板,规则DLL应用程序的模板,扩展DLL应用程序的模板,OLE/ActiveX应用程序的模板等。
这些模板都采用了以文档视图为中心的思想,每一个模板都包含一组特定的类。
为了支持对应用程序概念的封装,MFC内部必须做大量的工作。
例如,为了实现消息映射机制,MFC编程框架必须要保证首先得到消息,然后按既定的方法进行处理。
又如为了实现DLL编程的支持和多线程编程的支持,MFC内部使用了特别的处理方法,使用模块状态、线程状态等来管理一些重要信息。
虽然,这些内部处理对程序员来说是透明的,但是,懂得和理解MFC内部机制有助于写出功能灵活而强大的程序。
MFC提供了一个Windows应用程序开发模式,对程序的控制主要是由MFC框架完成的,而且MFC也完成了大部分的功能,预定义或实现了许多事件和消息处理等。
框架或者由其本身处理事件,不依赖程序员的代码;或者调用程序员的代码来处理应用程序特定的事件。
MFC是C++类库,程序员就是通过使用、继承和扩展适当的类来实现特定的目的。
例如,继承时,应用程序特定的事件由程序员的派生类来处理,不感兴趣的由基类处理。
实现这种功能的基础是C++对继承的支持,对虚拟函数的支持,以及MFC实现的消息映射机制。
3.1.3MFC的类库层次
MFC下的各类分别处理不同的工作,有的负责显示,有的负责创建窗口,有的负责绘图,有的集中控制有关控制的工作。
MFC已成为一个包罗Windows方方面面的编程工具,其体系结构的复杂是可想而知的,但MFC类库中各类的严格按照面向对象的技术派生出来的,而且可以分为各个功能块,即每个类都将完成自己特定的功能,所以MFC虽然庞大又复杂,但它在结构和逻辑上都是条理清晰的,MFC可分为类、宏和全局函数3个主要组成部分。
类是MFC中最主要的内容。
MFC的类构成一个完整的体系,该体系由一个被称为CObject的类作为基类,其他类大部分由该类派生而来。
这样整个体系包括若干个用于管理一个Windows应用程序的不同方面的子层次,这些子层次包括窗口(window)类、图形设计接口(GDI)类、对象链接和嵌入(OLE)类、文件和数据库类、对象输入/输出类、异常类、集合类和混杂的支持类。
●1.CObject类
CObject类是MFC主体的基类,其他大部分MFC类都是从CObject类中派生出来的。
作为基类,要为派生类打下良好的基础,所有CObject提供下列基础服务:
(1)序列化技术(支持存储对象的成员)。
(2)运行时类信息(支持动态创建对象)。
(3)对象诊断输出(调试程序所必不可少的特点)。
(4)与集合兼容性(包括兼容数组、列表、映像)。
(5)从CObject类派生出来的类都具有上述这些特点。
●2.应用程序结构类
有很多类是用来创建程序核心对象的,这就是应用程序结构类。
CCmdTarget类是MFC中拥有消息循环和消息处理机制的基类。
CCmdTarget类中包含了Windows事件驱动程序设计的概念。
CCmdTarget派生出了应用程序框架中的许多重要的类,如CWinApp、CFrameWnd、CDocument、CView等。
如果用户需要创建一个能处理消息的类,也可以从CCmdTarget中创建。
●3.调试和异常类
CException是从CObject直接派生出来的专门处理调试和异常的基类。
正确地处理程序中的异常是程序可靠的重要合保证,所以健全的程序设计中一定要有异常处理。
●4.文件服务类
文件服务类CFile是直接由CObject类派生出来的基础文件服务的基类。
CFile类直接支持非缓冲二进制磁盘文件的输入/输出,同时支持文本文件的输入/输出,还支持内存文件映射。
CFile与CAchive结合可以支持MFC对象的序列化。
CFile类及其派生类将文件操作格式统一,也就是说,对于所有种类的文件,MFC提供的服务界面都是一样的。
CFile的派生类还支持OLE流和通行于Internet上的HTML语言文件流。
文件列表功能也是由CObject的派生类CRecentFileList实现的,CRecentFileList类也可以算作文件服务类中的一员。
●5.图形处理类
图形类是MFC中活跃Windows表现的重要部分,它封装了属于GUI.DLL的API。
它们被用来支持各种图形设备环境,正确地使用图形类可以使用户的程序最大限度地发挥Windows图形系统的功能。
●6.数据库类
在MFC中支持两类数据库类:
一类是ODBC,另一类是DAO。
DAO是微软自己的数据库引擎,使用一套微软的数据访问对象,提供对Access数据库和VisualBasic数据库的支持。
ODBC是一套将多种数据库集成统一管理的API。
用户程序用这套API调用ODBC驱动管理程序。
驱动程序与数据库管理系统(DBMS)用SQL语言信息。
MFC提供了两种数据库引擎的支持类:
CDatabase和CDaoDatabase。
●7.同步类
同步类CSyncObject是一个虚类,用于多线程程序控制中。
同步对象使得运行在一个进程内的多个线程能协调工作。
多线程协调工作,对于多线程应用程序是很重要的,特别是当多线程访问相同的资源时,包括同一个文件或相同数据结构。
一个线程在其他线程不知道的情况下更新了数据,这可能使其他线程混淆新旧数据。
MFC提供了一系列CSyncObject的派生类来解决这一问题。
●8.集合类
MFC类库的建立缺不了基础数据结构的支持,如数组、列表和散列表,所以集合类中定义了数组类、列表类和映像类,分别支持上述三种数据结构。
●9.Internet类
MFC将支持Internet的API封装起来,用相应的类支持FTP、HTTP等协议的连接,并支持远程文件检查。
其中包括以下几类。
(1)CInternetSession类:
用来创建和初始化一个或多个同步的Internet进程。
如果需要,可以用来描述通过代理服务器的连接。
(2)CInternetConnection类:
用于管理对一个internet服务器的连接。
(3)CFileFind类:
实现本地的文件搜索,它是CFtpFileFind和CGopherFileFind的基类,它们实现在Internet上的文件搜索。
(4)CGopherLocator类:
可以从Gopher服务器上得到一个Gopher定位器,确定其类型,以供CGopherFileFind使用。
●10.非CObject派生类
MFC中Cobject派生出了大部分类,但MFC中还有许多重要的类非Cobject派生。
非Cobject派生类包括:
(1)Internet服务器应用程序接口类,完成ISAPI(InternetServerAPI),用来创建动态链接库,给Internet服务器和网页增加功能。
(2)动态对象模型支持类:
用来支持动态对象模型。
(3)简单数据类型类:
定义点、矩阵、尺寸、字符串、时间等常用简单数据类型。
(4)结构类:
封装了MFC中常用的结构。
宏是MFC提供的一个特色功能,MFC宏也是MFC的重要组成部分。
MFC的基本宏提供消息映射、运行时对象类型服务、诊断服务、异常处理等功能。
在MFC中,若某个函数或变量不是某个类的一个成员,那么就是一个全局函数或全局变量,它们可以在MFC源程序中任何地方被使用。
MFC约定全局函数以Afx为前缀,全局变量以afx为前缀。
常见的全局函数有AfxGetApp(获得应用程序对象的指针)、AfxGetInstanceHandle(获得当前运行实例的句柄)、AfxGetMainWnd(获得程序主窗口的指针)和AfxMessageBox(显示一个提示信息对话框)等。
3.1.4MFC程序结构剖析
下面以一个简单的MFC程序为例,对MFC程序的结构进行分析,以利于更深刻地认识MFC。
【例3.1】将第1章中的例2.2进行改写,工程名为Painting。
(1)点击File|new,在Project卡片下选择Win32Application,取工程名为Painting,按OK键。
(2)选择创建一个的空工程。
(3)在工作区中选取卡片FIleView,点击File|New,选C++SourceFile,创建名为Painting.cpp的源程序文件。
(4)将以下代码复制到源程序文件Painting.cpp之中。
#include
classCMyWnd:
publicCFrameWnd
{public:
CMyWnd(){}
protected:
afx_msgvoidOnPaint();
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyWnd,CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
voidCMyWnd:
:
OnPaint()
{CPaintDCdc(this);
dc.SelectStockObject(LTGRAY_BRUSH);
dc.Ellipse(150,50,200,150);
dc.SelectStockObject(DKGRAY_BRUSH);
dc.RoundRect(50,120,100,200,15,15);
dc.SelectStockObject(HOLLOW_BRUSH);
dc.Pie(250,50,300,100,250,50,300,50);
}
classCMyApp:
publicCWinApp
{
public:
BOOLInitInstance();
};
BOOLCMyApp:
:
InitInstance()
{
CMyWnd*pFrame=newCMyWnd;
pFrame->Create(0,("My_Windows"));
pFrame->ShowWindow(m_nCmdShow);
this->m_pMainWnd=pFrame;
returnTRUE;
}
CMyAppThisApp;
(5)选菜单Project|setting,在Projectsetting对话框中的General页中,在MicrosoftFoundationClass选项中选择UseMFCinastaticLibrary或UseMFCinasharedDLL,就是告诉系统要用MFC。
(6)编译、链接运行这个程序,其效果与例1.2相同(如下图所示)。
这表明,这个用MFC编写的程序,与例1.2利用WindowsAPI编写的程序是等效的。
而程序的代码量却减少了许多。
下面来分析这个程序的结构。
在程序的开始,由CWinApp派生出一个自己的类CMyApp。
这个类利用CWinApp中的虚拟初始化函数,改写成自己的初始化函数。
动态地创建了CMyWnd类来定义窗口和显示窗口。
而CWinApp自身去完成建立消息循环的工作。
从上述例程中可以得到以下结论。
(1)MFC的编程大大简化了Windows程序的开发。
(2)在MFC编程结构中CWinApp的作用非常重要,它相当于winmain()函数。
(3)在CWinApp中,函数InitInstance()的作用举足轻重,它是MFC应用程序入口点。
(4)CFrameWnd类负责窗口的创建和显示。
(5)声明应用程序对象时,应用程序自动运行,因为类的构造器中调用了Run()函数,在Run函数中建立消息循环机制。
InitInstance()函数是可以重载的,用户可以用它编写自己应用程序的初始化,InitInstance()函数完成以下工作。
(1)定义框架窗口(CFrameWnd)为应用程序的主窗口。
(2)显示主窗口。
(3)建立局部窗口与应用程序对象的关系。
3.2设备环境
在Windows这样一个图形界面操作系统中,图形操作是十分重要的。
图形是Windows的主体。
所有的信息,包括文本,最终都由图形来实现。
在Windows程序中,并不是把图形直接画到硬件上去,而是使用一个与硬件无关的设备环境(devicecontext,DC)来表示逻辑设备。
简单地说,设备环境抽象了不同的硬件环境为标准环境,用户编写时使用的是这个虚拟的标准环境,而不是真实的硬件,与真实硬件打交道的工作一般交给了系统和驱动程序完成。
在DC和设备驱动程序之间是一个隔离层—GDI。
GDI(graphicsdeviceinterface)是Windows提供的一个称为图形设备接口的抽象接口,用来完成所有图形的输出工作,GDI为Windows提供了所有的基本绘图函数。
在Windows的GDI函数中,使用设备环境作为一个参数。
GDI通过调用相应设备的驱动程序来响应Windows绘图函数的调用。
这种机制实现了与硬件无关性,即无需自己编写底层驱动程序代码,因为Windows本身已经包含了这些驱动程序。
设备环境也称为设备描述表,是由Windows管理的一个数据结构,是一个用来确定GDI输出位置和图形属性的集合(包括背景、调色板、字体、画笔等),也就是说它保存了绘图操作中一些需要设置的信息。
而真正的绘图函数是在GDI中。
设备描述表是Windows应用程序、设备驱动程序和输出设备(如打印机、绘图仪、显示器等)之间的桥梁,它定义了画图工具和画图信息。
下图显示了Windows应用程序经过设备描述表到设备驱动程序并最终到输出设备的信息流。
在Windows中,使用任何GDI绘图函数之前,首先必须建立设备环境。
设备环境是一个对窗口的绘图表面属性跟踪的数据结构。
这些属性包括用于在屏幕上绘图的当前所使用的画笔、画刷和字体以及颜色、前景等信息。
设备环境每次只能用一种画笔、一种画刷和一种字体。
例如,如果想用一个能够画粗线条的画笔,就必须创建一个新画笔,然后用它取代设备环境中原有的画笔。
类似地,如果想用红色的画刷填充一个图形,必须创建这个画刷,并将它选进设备环境。
这就是Windows程序员在设备环境中取代工具的方式。
在程序中不能直接存取设备环境结构,只能通过系统提供的一系列函数或使用设备环境的句柄HDC来间接地获取或设置设备环境结构中的各项属性,这些属性包括显示器的高度和宽度、支持的颜色数及分辨率等。
在Window图形系统的基础上,MFC将设备描述表(DC)和图形设备接口(GDI)封装在一起,建立了设备描述类—CDC类。
CDC类既包含了有关设备的属性,又包含了绘图方法,程序员只要发出合理的请求,CDC类便可以很方便地完成绘图工作。
CDC类是直接从CObject派生出来的,是设备环境类的基类,其他MFC设备环境类都是CDC类的派生类,利用它可以访问整个显示设备和非显示设备(如打印机)。
CDC类主要功能是定义设备描述表对象,提供在显示器上绘图的打印机上输出图形的方法。
CDC类提供的方法可以实现GDI的所有图表输出,包括控制着色和调色板、处理坐标映像和转换、绘制多边形和区域、使用不同字体绘制正文和处理元文件等等。
MFC类库中还提供了与设备环境相关的几个CDC派生类。
CPaintDC类:
窗口的OnPaint()函数使用的设备环境类。
该类的构造函数自动调用BeginPaint函数,析构函数自动调用EndPaint函数。
CClientDC类:
窗口客户区的设备环境类,一般用来实时映射鼠标事件,例如,绘制图形。
CWindowDC类:
整个应用程序窗口。
既包括客户区,又包括非客户区。
CMetaFileDC类:
表示Windows图元文件的设备环境类。
Windows图元文件包含了一系列GDI绘图命令,这些命令用于重新建立相应的映像。
由于MFC提供了不同的类型的设备环境类,每一个类都封装了代表Windows设备描述表的句柄,因此,在程序中通过声明一个MFC设备环境类的对象就自动获取了一个设备环境,而当该对象被销毁时就自动释放了获取的设备环境对象。
3.3绘图模式
在Windows绘图程序中,绘图的最终效果,如前景色、背景色等,可以通过设置绘图模式来进行改变。
绘图模式实际上是指画笔的颜色以什么样的方式填充在已经有显示的设备上,两者怎样相结合。
绘图模式只能应用在光栅设备上,如显示器,不能使用在矢量设备上,如绘图仪。
绘图模式使用各种布尔运算对二进制光栅进行操作。
绘图模式的确定可以通过CDC类的成员函数SetROP2()来设置:
IntSetROP2(intnDrawMode)
其中参数nDrawMode即为欲使用的绘图模式,函数的返回值为原来的绘图模式。
Windows定义了多种绘图模式,表3.1给出了常用的模式。
表3.1绘图模式
绘图模式
绘图设定
R2_BLACK
用黑色笔绘图
R2_WHITE
用白色笔绘图
R2_NOP
用无色笔绘图
R2_NOT
用与背景色相反的颜色绘图
R2_NOTCOPYPEN
用与画笔色相反的颜色绘图
R2_COPYPEN
用画笔颜色绘图
R2_XORPEN
对画笔色和背景色做异或运算
3.4GDI坐标系
在绘图时,通过坐标系可以确定图形或文本的输出位置,所以建立一个合适的坐标系可以为绘图带来很大的方便。
●1.什么是GDI坐标系
首先从数学角度来看,GDI坐标
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第3章 MFC编程 MFC 编程
![提示](https://static.bingdoc.com/images/bang_tan.gif)