




已阅读5页,还剩31页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
VC+培训教程草稿(2000年撰写) 张孝祥、袁龙友著 网址:第4章 MFC应用程序框架剖析在进行一个Windows应用程序开发之前,首先需要选择一个开发环境。假定我们现在只给出两种选择:一种是使用Win32应用程序编程接口的C语言程序;另一种是使用MFC应用程序框架的C+语言程序。在这里,笔者并不想阐述这两种选择哪个更好,而是想通过这两种开发环境的比较让读者更好的理解本章将要讲述的重点MFC。4.1 什么是MFC以及MFC的特点只要使用过传统的windows编程方法开发应用程序的读者都深刻地体会到,即使是开发一个简单的windows应用程序也需要对windows的内部编程原理有很深刻的认识,同时也要手工编写很多的代码。用C直接编写Windows API应用程序,代码量是非常大的,因为你所要照顾的细节太多了,更令人头疼的是,随着代码行数的增多,程序的可读性、可维护性也大大的降低,因此,传统的windows编程是需要极大的耐心和丰富的编程经验的。近几年来,面向对象技术无论是在理论还是实践上都在飞速地发展。面向对象技术中最重要的就是“对象”的概念,它把现实世界中的人、机器等客观实体抽象成程序中的“对象”。这种“对象”具有一定的属性和方法,这里的属性指对象本身的各种特性参数。如人的体重,机器的体积等,而方法是指对象本身所能执行的功能,如人能行走,机器能运作等。一个具体的对象可以有许多的属性和方法,面向对象技术的重要特点就是对象的封装性,对于外界而言,并不需要知道对象有哪些属性,也不需要知道对象本身的方法是如何实现的,而只需要知道如何调用对象所提供的方法来完成特定的功能。从这里我们可以看出,当把面向对象技术应用到程序设计中时,程序员只是在编写对象方法时才需要关心对象本身的细节问题,大部分的时间是放在对对象的方法的调用上,组织这些对象进行协同工作。MFC的英文全称是Microsoft Fundation Classes,即微软的基本类库,它位于任何 Windows API 之上,在这个类库中包含了一百多个程序开发过程中最常用到的对象。它减少了大量在建立 Windows 程序时必须编写的代码。同时它还提供了所有一般 C+ 编程的优点,例如继承和封装。正是由于MFC编程方法充分利用了面向对象技术的优点,它使得我们在编程时极少需要关心对象方法的实现细节,而只需要简单地调用已有对象的方法就可以了。即使类库中的那些对象的方法不能满足我们的需要时,我们还可以利用面向对象技术中很重要的“继承”方法从类库中的已有对象派生出我们自己的对象,这时派生出来的对象除了具有类库中的对象的特性和功能之外,还可以由我们自己根据需要加上所需的特性和方法,产生一个更专门的,功能更为强大的对象。这使得应用程序中程序员所需要编写的代码大为减少,有力地保证了程序的良好的可调试性。MFC不仅给用户提供了Windows图形环境下应用程序的框架,而且还提供了创建应用程序的组件。下面,我们将会通过一个实例来验证MFC的特点。还记得我们在第二章中讲过的那个创建窗口的程序(EX02-00)吗?创建一个窗口然后在该窗口的顶端打印一条语句并响应鼠标键盘消息,读者大概不会忘了,为了实现这个功能所要做的那些烦琐的步骤,包括创建一个窗口类、注册类、创建窗口、刷新窗口、消息循环等等。下面,我们将利用MFC类库功能模仿AppWizard建立一个这样的程序,看看它是怎么实现的。请看清单4-00中的代码。如果你是第一次看到这类程序,也许比较难理解,这没关系,我们后面会详细介绍。现在你只要用Visual C+ 环境中建立、编译和运行它就可以了。建立一个Win32程序(实际上本节的这个程序并非一个真正W32程序,只是采用W32编程环境,利用MFC类库来实现)的过程忘了吗?步骤同第二章所讲述的一样,只不过代码不同。启动Visual C+,打开New Wizard对话框,选择Project选项卡,在左边的窗口中选择Win32 Application,在右边输入工程名,OK就可以了,然后从Project菜单中选择Add to Project | New,再次打开New Wizard对话框,在左边窗口选择C+ Source file,在右边输入和工程名相同的文件名就可以了,最后在代码编辑区中输入下面清单中的语句就可以编译运行了。完整的例程请参见光盘中的例子代码EX04-00。清单4-00 模仿MFC应用程序框架生成一个窗口/*/ MyApp.h/*#include afxwin.hclass CMyApp:public CWinApp public:virtual BOOL InitInstance();class CMyFrame:public CFrameWndpublic:CMyFrame();protected:afx_msg void OnPaint();DECLARE_MESSAGE_MAP();/*/ MyApp.cpp/*CMyApp theApp; /建立一个CMyAPP对象BOOL CMyApp:InitInstance ()m_pMainWnd=new CMyFrame();m_pMainWnd-ShowWindow (m_nCmdShow);m_pMainWnd-UpdateWindow ();return TRUE;BEGIN_MESSAGE_MAP(CMyFrame,CFrameWnd)ON_WM_PAINT()END_MESSAGE_MAP()CMyFrame:CMyFrame()Create(NULL,MYAPP Application);void CMyFrame:OnPaint ()CPaintDC dc(this);dc.TextOut (0,0,Hello World!); 编译上面的程序,报错!为什么呢?原来还没有添加MFC的支持,从Project菜单选定Setting对话框,选择General属性页,在Microsoft Foundation Classes标题下选择Use MFC in a Static Library一项就可以了。上面的程序只是模仿MFC应用程序框架生成的一个程序,实际上,若是真正用MFC应用程序框架来生成该程序,用户只需要键入清单中黑体的那一条语句,而其他那些语句,在用AppWizard应用程序向导生成框架时AppWizard已经自动为我们添加了。我们所要做的工作,只是找到OnDraw()或OnPaint()成员函数,键入pDC-TextOut(0,0,Hello World!);或dc.TextOut (0,0,Hello World!);一条代码就可以了。看出来了吧,使用 MFC 进行 Windows 编程时是一件多么容易的过程。综上所述,MFC的优点可归结为以下几点:l MFC是C+的Microsoft Windows 应用程序编程接口。l MFC开发的应用程序使用了标准化的结构。l 使用MFC开发的应用程序不仅小,而且运行快。l Visual C+工具降低了编码的复杂性。l MFC库功能丰富(具体功能请参见相关书籍)。 4.2 什么是MFC应用程序框架什么是应用程序框架(Application Frame)?大概有的读者还不了解。说白了,就是一个集合,而这个集合可以提供一般应用程序所需的全部面向对象软件组件。一个应用程序框架是一个类库的超级集合。一般的库是设计集成到任何程序类的集合,但是一个应用程序框架不同于单纯的类的集合,它自己定义了程序的结构。上面空洞的语句大概并不能使我们很好的了解到底什么是应用程序框架,那么,我们将通过使用AppWizard应用程序向导生成的一个最简单的程序来使大家理解到底什么是应用程序框架。1. 首先启动Visual C+,在“File”菜单中选择“New”选项, 打开New Wizard,如图4-00所示,在该窗体中选择Projects标签,然后在左侧窗口选择MFC AppWizard(exe),在右侧的Project Name(项目名)文本框中,为新建的项目输入一个名称,如Test。2. 单击OK按钮弹出MFC AppWizardStep1对话框,如图4-01所示。该对话框中要求用户选择生成哪种类型的应用程序:单文档(Single document)、多文档(Multiple documens)还是基于对话框(Dialog based)的应用程序。我们选择Single document,其他的按默认设置。需要用户了解的是如果我们选择Single document,那么应用程序会给我们生成一个单文档-视图应用程序框架;如果我们选择的是Multiple documens,那么应用程序会给我们生成一个多文档-视图应用程序框架图4-00 New Wizard图4-01 MFC AppWizardStep1对话框3. 单击NEXT按钮弹出MFC AppWizardStep2of6对话框,如图4-02所示。询问用户应用程序是否支持数据库:不支持数据库(None)、仅支持头文件(Header files only)、没有支持文件的数据库视图(Database view without file support)、带文件支持的数据库视图(Database view with file support)。我们选择None。图4-02 MFC AppWizardStep2of6对话框4. 单击NEXT按钮弹出MFC AppWizardStep3of6对话框,如图4-03所示。询问用户应用程序支持何种复合文档:不支持复合文档(None)、容器(Container)、能够创建管理复合文档但Mini-server应用程序不能单独运行(Mini-server)、能够创建管理复合文档并且能单独运行(Full-server)、用户应用程序既是容器又是服务器(Both container and server)。我们选择None,其他设置按默认。图4-03 MFC AppWizardStep3of6对话框图4-04 MFC AppWizardStep4of6对话框5. 单击NEXT按钮弹出MFC AppWizardStep4of6对话框,如图4-04所示。询问用户生成何种特征的应用程序:增加一个工具栏(Docking toolbar),增加一个状态栏(Initial status bar),生成打印和预览命令的代码(Printing and print preview),生成支持上下文相关帮助的帮助文件(Context-sensitive Help),支持三维效果(3D controls),使应用程序能够创建、操作、传输和存储邮件消息(MAPI Messaging API) ,支持Windows Sockets(Windows Sockets)。这里所有设置我们都采用默认的。6. 单击NEXT按钮弹出MFC AppWizardStep5of6对话框,如图4-05所示。询问工程的样式、是否生成源文件注释及怎样使用MFC类库。一切按默认设置。图4-05 MFC AppWizardStep5of6对话框图4-06 MFC AppWizardStep6of6对话框7. 单击NEXT按钮弹出MFC AppWizardStep6of6对话框,如图4-06所示。对话框中的默认设置确定了类的名称及其所在文件的名称,用户可以改变名称,但我们建议您使用向导自动生成的名称。在Visual C+中,AppWizard允许你选用MFC类库中其他视图类作为应用程序视图类的基类,不一定非得是默认的CView类,您可以改成CEditView类。这里,我们全部采用默认设置。8. 单击FINISH按钮弹出NewProject Information对话框,如图4-07所示。这个对话框主要显示创建的文件的清单。最后单击OK按钮,大功告成!不管您相不相信,我们已经生成了一个使用MFC基本类库的应用程序基本框架(单文档-视图体系结构的应用程序框架)。而且马上就可以运行。运行之后,您会发现这个应用程序框架具备WINDOWS程序的所有特性,菜单、工具条、状态栏、图标甚至还有关于对话框、打印预览.全了,这就是AppWizard通过MFC动态创建的一个应用程序框架。从这个小例子可以看出用VC/MFC设计WINDOWS程序是多么方便的了吧。也许读者对此很不理解,为什么没有编写一句代码就出来个应用程序框架,而且WinMain函数和WndProc回调函数都不见了?其实,WinMain函数和WndProc回调函数并没有不见,也不是Window程序结构发生了变化,这全都是MFC的功劳,我们这个应用程序框架的底层仍然采用的是WinMain函数和WndProc回调函数,只是它们被MFC封装在各个类里了,然后MFC通过应用程序向导为我们生成了这些类,我们所要做的就是往这个类里添加一些内容,如重载函数、消息处理等等。就好比MFC程序框架是一座大楼的主梁,主梁都有了,剩下程序员所要做的就是添砖加瓦。而在Win 32程序中,甚至主梁都要程序员自己搭建,是不是很累啊。图4-07 NewProject Information对话框现在 ,读者可以验证一下上一节中提到的只需要键入一条代码就可以在屏幕上显示出“Hello World!”。在工作台中,在Class View选择CTestView类的OnDraw()成员函数双击,填入如下一条代码:pDC-TextOut (0,0,Hello World!);运行一下,怎么样,是不是也显示出了Hello World字样,很简单吧,完整例程请参见光盘中的例子代码EX04-01。这就是MFC应用程序框架的好处。4.3 应用程序框架说明本节将对AppWizard向导自动生成的应用程序框架的内容做一个详细的介绍。让您对MFC方式的WINDOWS应用程序的工作原理有全面的认识,只有这样你才会知道应该如何往程序框架当中添加需要的代码。我们将从两方面做介绍:一个是文件;另一个是类。4.3.1 文件说明在使用Visual C+开发应用程序的过程中,系统为我们生成了大量的各种类型的文件,在本节中将要详细介绍Visual C+中这些不同类型的文件分别起到什么样的作用,在此基础上对Visual C+如何管理应用程序所用到的各种文件有一个全面的认识。首先要介绍的是扩展名为dsw的文件类型,这种类型的文件在VC中是级别最高的,称为Workspace文件。在VC中,应用程序是以Project的形式存在的,Project文件以.dsp扩展名,在Workspace文件中可以包含多个项目,由Workspace文件对它们进行统一的协调和管理。与dsw类型的Workspace文件相配合的一个重要的文件类型是以opt为扩展名的文件,这个文件中包含的是在Workspace文件中要用到的本地计算机的有关配置信息,所以这个文件不能在不同的计算机上共享,当我们打开一个Workspace文件时,如果系统找不到需要的opt类型文件,就会自动地创建一个与之配合的包含本地计算机信息的opt文件。上面提到Project文件的扩展名是dsp,这个文件中存放的是一个特定的工程,也就是特定的应用程序的有关信息,每个工程都对应有一个dsp类型的文件。以clw为扩展名的文件是用来存放应用程序中用到的类和资源的信息的,这些信息是VC中的ClassWizard工具管理和使用类的信息来源。对应每个应用程序有一个readme.txt文件,这个文件中列出了应用程序中用到的所有的文件的信息,打开并查看其中的内容就可以对应用程序的文件结构有一个基本的认识。在应用程序中大量应用的是以h和cpp为扩展名的文件,以h为扩展名的文件称为头文件。以cpp为扩展名的文件称为实现文件,一般说来h为扩展名的文件与cpp为扩展名的文件是一一对应配合使用的,在h为扩展名的文件中包含的主要是类的定义,而在cpp为扩展名的文件中包含的主要是类成员函数的实现代码。在应用程序中经常要使用一些位图、菜单之类的资源,VC中以rc为扩展名的文件称为资源文件,其中包含了应用程序中用到的所有的windows资源,要指出的一点是rc文件可以直接在VC集成环境中以可视化的方法进行编辑和修改。最后要介绍的是以rc2为扩展名的文件,它也是资源文件,但这个文件中的资源不能在VC的集成环境下直接进行编辑和修改,而是由我们自己根据需要手工地编辑这个文件。对于以ico,bmp等为扩展名的文件是具体的资源,产生这种资源的途径很多。使用rc资源文件的目的就是为了对程序中用到的大量的资源进行统一的管理。图4-08 工作台Class View4.3.1 类的说明打开工作台,我们可以看到系统在生成应用程序框架的过程中,自动为我们生成了这么几个类:CTestApp、CMainFrame、CTestView、CTestDoc。这些类名字中的test并不是固定的类名,而是我们起的工程的名字。如我们建立的工程名为abc,那么系统为我们生成的类名分别为CabcApp、CMainFrame、CabctView、CabcDoc。如图4-08所示。前面我们已经提到过,在应用程序中有一个重要的函数WINMAIN(),这个函数是应用程序的基础,用户的操作所产生的消息正是经过这个函数的处理派送到对应的对象中进行处理。在MFC方式的WINDOWS应用程序中,用来处理消息的是系统自动生成的MFC中的类CWINAPP的派生类CTestAPP,下面就从这个类开始介绍应用程序的框架。也请读者参照下面的讲解来自行分析一下清单4-00的代码。l 类CTestApp(基类为CWinApp)若想创建MFC应用程序,首先要使用CWinApp类,因为CWinApp类不仅代表了程序中运行的主线程,而且代表了应用程序本身,在任何MFC应用程序中只有一个CWinApp对象,它用于控制应用程序的初始化、启动应用程序、运行主消息循环、终止应用程序。当用AppWizard向导创建一个应用程序框架之后,应用程序框架自动生成了唯一的应用程序对象theApp。从表面上看它仅是某个类的一个实例,实际上它内部隐藏了许多细节,如隐藏了程序最开始执行的WinMain函数。CWinApp类有三个重要的成员函数分别为InitInstance()、Run()、ExitInstance(),其中InitInstance()成员函数是必须被重载的成员函数,AppWizard向导为我们生成的应用程序框架中的类CTestApp便重载了该函数,它负责进行特定应用程序的初始化,包括装入MRU列表。Run()函数是用来处理消息循环的,我们不建议用户去重载它,该函数在后面讲述消息的章节中会有详细的介绍。ExitInstance()函数是做收尾工作的,应用程序通过调用该函数来结束运行,用户可以重载该函数以完成特殊的清除要求,如释放内存。我们讲过InitInstance()成员函数是必须被重载的成员函数,因为CTestAPP就是由MFC中的类CWINAPP派生来的,因此CTestAPP不仅是AppWizard向导为我们自动生成的应用程序运行的基础,而且,在这个类中除了具有一般类都有的构造函数之外,还有一个重要的成员函数就是InitInstance,我们知道,在WINDOWS环境下面可以运行同一程序的多个实例,函数InitInstance的作用就是在生成的一个新的实例的时候,完成一些初始化的工作。当生成一个CTestApp类型的对象的时候,系统会主动调用InitInstance函数完成一些必要的初始化工作。应用程序类的另一个主要的功能就是通过文档模板CDocTemplate将框架窗口类、文档类及视图类联系起来。文档模板类是抽象类,因而不能直接使用,但可以使用它的两个派生类:针对于单文档的CSingleDocTemplate类和针对于多文档的CMultiDocTemplate类。此功能也是由应用程序类的InitInstance函数实现。下面研究InitInstance函数所做的事情,注意下面这一段代码,CSingleDocTemplate* pDocTemplate;pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CTestDoc),RUNTIME_CLASS(CMainFrame), / main SDI frame windowRUNTIME_CLASS(CTestView);AddDocTemplate(pDocTemplate);它定义了一个文档模板对象指针PDocTemplate,通过New操作符,系统动态生成了这个文档模板对象,然后使用AddDocTemplate函数把这个文档模板对象加入到应用程序所维护的文档模板链表当中,这个文档模板pDocTemplate的作用就是把程序用到的框架窗口,CMainFrame,文档CTestDoc,视窗CTestView与应用对象CTestApp联系起来。CTestApp类提供了用户与WINDOWS应用程序之间进行交流的界面。在生成这个类的对象后,这个对象自动地把自身与WINDOWS系统建立联系,接收WINDOWS传送的消息,并交给程序中相应的对象去处理,这就免去了程序员许多的工作,使得开发C+的WINDOWS程序变得简单方便。l 类CMainFrame(基类为CFrameWnd)类CMainFrame是由MFC中的CFRAMEWND派生来的,所以它也是一个框架窗口。主框架窗口类负责标题栏、菜单栏工具栏及状态栏的生成。事实上,这些子窗口是可以按照用户的意图自定义的,这只需要修改主框架窗口的实现文件即可。CMainFrame是CTestView的父类,也就是说视窗VIEW显示在主框窗口MAINFRAME的客户区中。在类CMainFrame中,系统已经从类CFrameWnd那里继承了处理窗口的一般事件的WINDOWS消息,比如改变窗口的大小,窗口最小化等等的成员函数,因此编程的时候程序员不需要再关心此类消息的处理,从而减轻了程序员的负担。当然,如果确实需要重新编写处理此类消息的成员函数,则需要对原有的成员函数进行重载。在MFC程序中,由于CTestView类是覆盖在CMainFrame类窗口之上的,因此我们并不需要经常对CMainFrame类进行操作,更多的是对视窗类进行操作,达到对程序中的数据进行编辑和修改的目的。最后要指出的是,在MFC方式的程序中,当程序的一个实例被运行的时候,系统根据前面在CTestApp类中介绍的文档模板对象自动生成类CMainFrame,CTestView,CTestDoc的对象,而不需要程序员主动地去创建这些类的对象。另外,应用程序框架通过主框架窗口与视图对象、文档对象利用消息进行互操作,如主框架窗口访问视图对象的方法是调用GetActiveView成员函数,而访问文档对象的方法是调用GetActiveDocument成员函数。l CTestView类(基类为CView)和CTestDoc类(基类为CDocument)之所以把CTestView类和CTestDoc类一起介绍是因为这两个类是密切相关的。视图是显示文档数据的界面,它主要占据着主框架窗口的客户区,视图类不仅把程序文档数据显示出来,它还能接受用户的输入、编辑。视图对象也通过消息与主框架、文档对象互操作。文档对象是存储程序数据的地方。一般而言,把要存储的数据放到文档对象中,即把要存储的数据作为文档对象的成员变量,进而实现文档的串行化。下面的框图4-09可以说明文档与视窗的关系。应用程序对象文档模板文档工具条视窗主框架窗口图4-09 视图和文档类的关系在这个框图当中,文档是由文档模板(Document Template)对象生成的,并由应用程序对象管理,而用户则是通过与文档相联系的视窗对象来存储、管理应用程序的数据,用户与文档之间的交互则是通过与文档相关联的视窗对象来进行的。生成一个新的文档的时候,MFC程序同时生成一个框架窗口,并且在框架窗口的客户区中生成一个视窗对象作为框架窗口的子窗口,这个子窗口以可视化的方式表现文档中的内容。视窗的重要功能就是负责处理用户的鼠标、键盘等操作,通过对视窗对象的处理达到处理文档对象的目的。总之,类CTestView的作用是显示数据,而数据的来源是类CTestDoc,在MFC程序中,程序的数据是放在文档当中的,而显示数据则是利用视窗方式,文档与视窗分离带来的好处就是一个文档可以同时具有多个视窗,每个视窗可以只显示文档中的一部分数据,或者以特定的风格显示文档中的数据。文档与视窗分离的另一个好处就是在程序中可以处理多个文档,通过对不同的视窗的处理达到对不同的文档分别处理的目的。要指出的一点是,WINDOWS应用程序框架通常分为单文档-视图应用程序框架SDI和多文档-视图应用程序框架MDI两种,在单文档界面中,文档窗口与主框架窗口是同一概念。而这时的视窗对象则是显示在文档窗口的客户区当中。我们先前生成的Test程序使用的就是单文档界面方式,此时文档窗口是主框架窗口,即类CMAINFRAME的对象。关于文档-视图体系结构在下一节会有详细介绍。以上介绍了应用程序框架的结构以及各类之间的关系,请读者参照图4-10的结构好好的理解一下本节的内容,掌握MFC应用程序框架是做好MFC编程的基础。图4-10 MFC应用程序的主要对象之间的关系4.4 文档-视图体系结构文档-视图(Document-View)体系结构是MFC的一大特色,它将数据的管理和显示隔离;文档对象是用来容纳数据的,而视图对象则是用来显示数据并且对数据进行编辑。本节将介绍文档怎么样维护应用程序的数据,怎样向用户显示这些数据以及在应用程序运行时文档和视图对象之间是怎样进行对话的。4.4.1 文档-视图相互作用的函数在文档、视图和应用程序框架之间包含了一系列非常复杂的相互作用过程。为了了解这一过程,首先要熟悉有关文档类和视图类中的几个重要的成员函数。1. CView:GetDocument()函数一个视图对象有且只有一个与之相关联的文档对象,它所包含的GetDocument()函数允许应用程序在视图中得到与之相联系的文档对象。假设一个视图对象得到一个消息表明用户已经在编辑控制中输入了新的数据,那么视图必须告诉文档对象相应地更新它的内部数据。GetDocument()函数返回的是指向文档的指针,利用这个指针我们就可以访问文档类或其派生类的成员函数及公共数据成员。当AppWizard生成一个派生的CView类的时候,它同时也创建了一个特殊的typesafe(保护类型)的GetDocument()函数,它返回的不是一个CDocument指针, 而是一个指向其派生类对象的指针。该函数是一个内联函数,形式如下: CTestDoc* CTestView:GetDocument() / non-debug version is inline ASSERT(m_pDocument-IsKindOf(RUNTIME_CLASS(CTestDoc);return (CTestDoc*)m_pDocument;当编译器在视图类代码中看到一个GetDocument调用时,它用派生类版本来代替GetDocument版本,这样就不需要把返回的指针转向派生的文档类。这是因为CView的GetDocument函数不是一个虚函数,下面的语句:pView-GetDocument();调用基类GetDocument函数,这样返回的是一个指向CDocument对象的指针。2. CDocument:GetNextView()函数一个文档对象可以和多个视图对象相联,CDocument对象的GetNextView成员函数用来在文档中得到指向视图列表的指针,但是由于一个文档拥有不止一个视图,所以,文档对象有必要通过这个指针遍历视图列表,对每个视图都调用一次这个成员函数。这时常用到CDocument类的另一个成员函数GetFirstViewPosition()与GetNextView()函数配合使用。GetFirstViewPosition函数返回文档对象中视图列表中的第一个视图的位置,即POSITION值,而GetNextView负责返回下一个视图,程序的样式通常如下:CTestDoc *pTestDoc; /CTestDoc为CDocument的派生类CTestView *pTestView /CTestView为CView的派生类pTestDoc=pTestView-GetDocument();POSITON pos=pTestDoc-GetFirstViewPosition();While (pos!=NULL)pTestView=(CTestView*)pTestDoc-GetNextView();/对视图进行操作3. CDocument:UpdateAllViews()函数如果由于某种原因文档数据发生了变化,那么必须通知所有的视图以便它们能够对所显示的数据进行相应的更新。这时就要用到UpdateAllViews函数了。如果UpdateAllViews是从一个派生文档类的一个成员函数中调用,那么它的第一个参数pSender的值为NULL ;如果在视图派生类的成员函数中调用UpdateAllViews,那么应该把 pSender参数的值设为当前视图,如下所示:GetDocument()-UpdateAllViews(this);其中非空参数使得应用程序框架不再通知当前视图,这里是假定当前视图已经更新了自己。函数有可选提示参数,用来给出专门视图和应用程序决定的关于更新视图的哪部分的消息。这是该函数的一个高级用法,该函数的基本形式如下:void UpdateAllViews(CView* pSender,LPARAM lHint=0L,CObject* pHint =NULL);其中lHint 和pHint就是提示参数,可以利用它们给视图提供一些特殊的与应用程序有关的信息,以便视图能决定哪些部分应该更新。4. CView:OnUpdate()函数当UpdateAllViews()被调用时,视图是怎样被通知的呢?就是利用CView 类的OnUpdate函数。OnUpdate函数是一个虚函数,当应用程序调用了UpdateAllViews函数时,应用程序框架会相应的调用所有视图的OnUpdate函数。当然,我们也可以在派生的CView类中直接调用这个函数。通常情况是,派生视图类的OnUpdate函数访问文档,得到文档的数据,然后更新视图数据的成员或控制来反映这些变化。作为选择,OnUpdate可以使视图的一部分无效,导致视图的OnDraw函数使用文档数据来在窗口中重画。OnUpdate函数的基本形式如下:void CTestView:OnUpdate(CView* pSender,LPARAM lHint=0L,CObject* pHint =NULL)CTestDoc* pTestDoc=GetDocument();ASSERT_VALID(pTestDoc);/加入视图的初始化代码其中lHint和 pHint两个提示信息是UpdateAllViews函数直接传递过来的。默认的OnUpdate函数将使整个窗口无效,我们可以重写该函数,可以选择定义一个由提示信息具体说明的较小的无效矩形。5. CView: OnInitialUpdate()函数当应用程序启动,或当用户从File菜单中选择了New或Open时,CView类的OnInitialUpdate函数会被调用。该函数是一个虚函数。基类CView类的OnInitialUpdate函数版本除了调用OnUpdate之外不做任何事情;如果在派生的视图类中重载了这个函数,要确保视图类调用了基类的OnInitialUpdate函数或派生类的OnUpdate函数。我们可以利用派生类的OnInitialUpdate函数对视图对象进行初始化。当应用程序启动后,应用程序框架在调用OnCreate之后立即调用OnInitialUpdate(前提是在视图类中映射了OnCreate)。OnCreate函数只能被调用一次,而OnInitialUpdate函数可以被调用多次。 6. CDocument:OnNewDocument()函数在一个文档对象首次被构造之后并且用户从一个SDI应用程序的File菜单中选择了New的时候,框架调用这个虚拟函数。这是初始化文档对象的数据成员的好地方。AppWizard在派生文档类中生成一个无效的OnNewDocument函数,别忘了保留对基类函数的调用。7. CDocument:DeleteContents()函数当我们需要一个函数用来删除文档的内容时,一方面我们可以自己编写一个私有成员函数进行删除;另一方面应用程序框架为CDocument类定义了一个DeleteContents函数。当文档被关闭的时候,应用程序框架会调用用户重载的DeleteContents函数,进行文档的清理工作。这里,简单的总结一下视图与文档的交互流程:当用户在视图中对文档数据进行编辑时,视图会调用他自己的成员函数GetDocument来通知文档对象更新其内部数据;当文档对象修改数据之后,它会调用文档类的UpdateAllViews成员函数通知所有与之联系的视图进行强制更新显示,实际上,当应用程序调用了CDocument:UpdateAllViews时,应用程序框架会紧接着自动调用CView类OnUpdate函数,这个OnUpdate函数会先访问文档的数据,然后对视图进行更新。在默认情况下,OnUpdate函数使整个窗口矩形失效,从而触发视图的OnDraw函数,进而重绘窗口。4.4.2 单文档-视图应用程序结构一个单文档界面应用程序只有一个窗口。如果应用程序依赖于磁盘文件“文档”,那么一次只能加载一个文档,类似于Windows的“记事本”程序。当我们创建的是单文档界面应用程序时,AppWizard的会为我们创建四个类:CTestApp、CMainFrame、CTestView、CTestDoc,分别为CWinApp类、CFrameView类、CDocument类和CView类的派生类,这在上节已经提到过。4.4.3 多文档-视图应用程序结构一个多文档界面应用程序拥有多个子窗口。每个窗口单独对应于一个文档,类似于Windows的“记事本”程序。当我们创建的是多文档界面应用程序时,AppWizard的会为我们创建五个类:CTestApp、CMainFrame、CChildFrame、CTestView、CTestDoc,分别为CWinApp类、CMDIFrameWnd类、CMDIChildWnd类、CDocument类和CView类的派生类。4.5 MFC消息映射机制还记得第二章学过的Win32程序吗?在Win32应用程序中采用的是消息循环机制,用一系列的switch-case语句来处理消息的,而在MFC中,对消息的处理是采用消息映射机制。本节就将对消息及MFC的消息映射机制做一个深入的讲解。4.5.1 消息的种类在WINDOWS程序设计中,消息是个极为重要的概念,用户通过窗口界面的各种操作最后都转化为发送到程序中的对象的各种消息,下面就向您介绍在WINDOWS程序设计中最常用的一些消息:1 键盘消息l WM_CHAR,该消息的处理函数OnChar()l WM_KEYDOWN,用户按下一个非系统键l WM_KEYUP,在非系统键被释放时产生这三个消息用来处理用户的键盘数据,当用户在键盘上按下某个键的时候,会产生WM_KEYDOWN消息,释放按键的时候又回产生WM_KEYUP消息,所以WM_KEYDOWN与WM_KEYUP消息一般总是成对出现的,至于WM_CHAR消息,是在用户的键盘输入能产生有效的ASCII码时才会发生。这里特别提醒要注意前两个消息与WM_CHAR消息在使用上是有区别的。在前两个消息中,伴随消息传递的是按键的虚拟键码,所以这两个消息可以处理非打印字符,如方向键,功能键等。而伴随WM_CHAR消息的参数是所按的键的ASCII码,ASCII码是可以区分字母的大小写的。而虚拟键码是不能区分大小写的。三种消息原型分别如下:afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);2 鼠标消息l WM_MOUSEMOVE,用户将鼠标移进窗口或在窗口中移动l WM_LBUTTONDOWN,用户按下左键l WM_LBUTTONUP,用户释放左键l WM_LBUTTONDBCLICK,用户双击左键l WM_RBUTTONDOWN,用户按下右键l WM_RBUTTONUP,用户释放右键l WM_RBUTTONDBCLICK,用户双击右键这组消息是与鼠标输入相关的,WM_MOUSEMOVE消息发生在鼠标移动的时候,剩余的六个消息则分别对应于鼠标左右键的按下、释放、双击事件,要指出的是WINDOWS系统并不是在鼠标每移动一个像素时都产生MOUSEMOVE消息,这一点要特别注意。消息原形分别如下:afx_msg void OnMouseMove(UINT nFlags, CPoint point);afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnLButtonUp(UINT nFlags, CPoint point);afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);afx_msg void OnRButtonDown(UINT nFlags, CPoint point);afx_msg void OnRButtonUp(UINT nFlags, CPoint point);afx_msg void OnRButtonUp(UINT nFlags, CPoint point);3 与窗口消息l WM_CREATE,窗口被创建l WM_DESTROY,窗口被销毁l WM_CLOSE,窗口被关闭l WM_MOVE,窗口发生移动l WM_SIZE,窗口发生改变l WM_PAINT,窗口发生重绘我们创建一个窗口对象的时候,这个窗口对象在创建过程中收到的就是WM_CREATE消息,对这个消息的处理过程一般用来设置一些显示窗口前的初始化工作,如设置窗口的大小,背景颜色等,WM_DESTROY消息指示窗口即将要被撤消,在这个消息处理过程中,我们就可以做窗口撤消前的一些工作。WM_CLOSE消息发生在窗口将要被关闭之前,在收到这个消息后,一般性的操作是回收所有分配给这个窗口的各种资源。在windows系统中资源是很有限的,所以回收资源的工作还是非常重要的。当窗口移动的时候产生WM_MOVE 消息,窗口的大小改变的时候产生WM_SIZE消息,而当窗口工作区中的内容需要重画的时候就会产生WM_PAINT消息。消息原形分别如下:afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);afx_msg void OnDestroy();afx_msg void OnClose();afx_msg void OnMove(int x, int y);afx_msg void OnSize(UINT nType, int cx, int cy);afx_msg void OnPaint();4 焦点消息l WM_SETFOCUS,窗口得到焦点l WM_KILLFOCUS,窗口失去焦点当一个窗口从非活动状态变为具有输入焦点的活动状态的时候,它就会收到WM_SETFOCUS消息,而当一个窗口失去输入焦点变为非活动状态的时候它就会收到WM_KILLFOCUS消息。消息原形分别如下:afx_msg void OnSetFocus(CWnd* pOldWnd);afx_msg void OnKillFocus(CWnd* pNewWnd);5 定时器消息:WM_TIMERWindows定时器是一种周期性消息产生装置,当我们为一个窗口设置了定时器资源之后,系统就会按规定的时间间隔向窗口发送WM_TIMER消息,在这个消息中就可以处理一些需要定期处理的事情。定时器消息的响应函数是OnTimer,原形如下:afx_msg void OnTimer(UINT nIDEvent);对于定时器的操作,通常都会和函数SetTimer()配合使用。在响应OnTimer 函数之前,应先添加一个定时器对其进行触发,如果没有建立定时器,系统是不
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 心包穿刺术详解
- 县人民医院信息化建设
- 新能源乘用车解读
- 赛犬喂养指南解读
- 数控加工技术工艺
- 手电筒幼儿讲解
- 现代农业测控技术
- 售前跟单技巧讲解
- 小学课文印刷技术
- 七下显微镜讲解
- 带状疱疹护理查房
- SX-22163-QR345工装维护保养记录
- 中国重症加强治疗病房建设与管理指南
- 2023年航空职业技能鉴定考试-候机楼服务技能考试题库(含答案)
- MBA培训进修协议
- p型半导体和n型半导体课件
- LY/T 2501-2015野生动物及其产品的物种鉴定规范
- GB/T 748-2005抗硫酸盐硅酸盐水泥
- GB 15763.1-2001建筑用安全玻璃防火玻璃
- 民间文学(全套课件)
- 既有重载铁路无缝线路改造及运维技术探索
评论
0/150
提交评论