1、用MFC设计Windows应用程序完成带图片第1章 Windows应用程序基础知识Windows是一种应用于微型计算机的操作系统,它为应用程序提供了一个多任务环境,这个环境具有一致的图形化窗口和菜单。在Windows操作系统上运行的应用程序叫做Windows应用程序。1.1 Windows应用程序的基本概念1.1.1 Windows应用程序的界面Windows应用程序的一个突出特点是它有一个美观的图形用户界面(GUI)。这个图形界面是Windows应用程序与用户交换信息的一个“窗口”。简单的Windows应用程序只有一窗口,而复杂的Windows应用程序可能需要多个窗口。因此,设计一个或一组满
2、足要求的窗口,是Windows应用程序设计中的一项重要任务。1.1.2 Windows应用程序与API函数为了设计Windows应用程序,Windows提供了大量预定义的用C语言编写的函数,这些函数叫做API(Application Programming Interface)函数。API函数几乎包含了Windows程序中所有操作功能。以前用来设计Windows应用程序的开发工具是SDK(Software Development Kit,软件开发工具包),使用SDK来设计Windows应用程序,实际上就是在使用API函数来开发应用程序。自从出现了面向对象程序设计方法以来,人们用类对Window
3、s API函数进行了封装,从而使用应用程序开发工具发生了巨大的变化。目前,用来设计Windows应用程序的开发工具大多是“面向对象”的而且是“可视的”,如Visual C+、Visaul Basic、Java等。1.1.3 Windows的一个特殊数据类型句柄在Windows应用程序中,存在着很多诸如窗口、按钮、滚动条等复杂的对象和实例,为了在程序中区分和使用它们,必须对它们进行标识。为了区别于普通变量的标识,Windows把这种复杂对象的标识叫做“句柄”。不同的Windows对象的句柄具有不同的类型,常用的句柄类型见表1-1。表1-1 常用的句柄类型句柄类型说明HWND窗口句柄HINSTAN
4、CE当前应用程序实例句柄HCURSOR光标句柄1.1.4 消息与消息循环1.事件与消息Windows应用程序的另一个突出特点是在程序启动并创建了程序窗口之后,随即就进入一个等待状态(利用一个while循环),直到接收了某种刺激(如键盘输入、鼠标单击或双击)之后,程序才会跳出等待状态对这个刺激进行处理,而处理完毕后又进入等待状态。这些可能触发计算机程序作出相应反应的刺激,叫做“事件”。为了描述该事件中的各种信息(例如何时、何地发生了何种事件等),Windows定义了一个结构,这个结构就叫做“消息(message)”。当发生某种事件时,Windows系统就用消息来向应用程序发出通知。消息结构的定义
5、如下:typedef struct tagMSG HWND hwnd;/被检索消息的窗口句柄 UINT message;/消息的标识码 WPARAM wParam;/消息的附加信息1 LPARAM lParam;/消息的附加信息2 DWORD time;/消息进入消息队列的时刻 POINT pt;/表示发送该消息时光标的位置MSG;Windows系统就是根据消息来处理各种各样的事件。Windows系统已经把绝大多数事件的消息事先进行了定义,并定义了message的标识码,以便程序对消息进行识别。如:WM_LBUTTONDOWN(按下鼠标左键时产生的消息)。2.消息循环系统为程序建立了叫做消息队
6、列的一个存储空间,在程序的运行过程中如果发生了一个事件,Windows就会把这个事件所对应的消息送入消息队列等待使用。应用程序为了取得消息队列中的消息,需要调用Windows提供的一个API函数GetMessage,并利用这个函数的返回值组织一个循环来不断获取消息,一旦获取到消息,就这把个消息派送给系统。这个循环叫做消息循环,在Windows应用程序中,消息循环的代码如下:while(GetMessage(&msg,NULL,NULL,NULL) TranlateMessage(&msg); DispatchMessage(&msg);当消息循环把消息派送给系统后,系统会以消息作为参数来调用一
7、个叫做“窗口函数”的函数,然后以消息中的message成员为依据在这个函数中查找并执行该消息所对应的程序段。在处理完与消息对应的程序段之后,只要该消息不是终止应用程序的消息,应用程序就会立即返回消息循环,以等待获取下一个消息。Windows应用程序就这样周而复始进行循环,直至用户发出结束应用程序的消息。正是由于Windows应用程序必须接收到消息才会启动某种操作,因此人们常说,Windows应用程序是消息驱动的(或者说是事件驱动的)。1.2 Windows应用程序的结构1.2.1 主函数为了与DOS C程序的主函数名称相区分,Windows应用程序的主函数名称为WinMain。主函数主要完成两
8、个任务:一是创建应用程序的界面窗口;二是建立前面提到过的消息循环。创建应用程序窗口要用到4个API函数(描述窗口属性的数据结构窗口类、注册窗口类、创建窗口、显示窗口);建立消息循环要用到3个API函数(GetMessage、Translate、DispatchMessage)。1.2.2 消息的处理部分窗口函数具有窗口界面的Windows应用程序,必须要有一个“窗口函数”,各种消息的处理就是在这里完成的,它是完成用户任务的核心,也是需要程序员编写大量大代码的地方。LRESUALT CALL BACK WndPro(HWND hwnd,UINT message,WPARAM wParam,LPA
9、RAM lParam)switch(message) case WM_LBUTTONDOWN: case WM_DESTROY:PostQuitMessage(0);break; default:return DefWindowro(hwnd,message,wParam,lParam);return 0; 第2章 Windows应用程序的类封装人们用C+类把Windows的大多数API函数和对应的数据进行封装,并把这些类组织成类库MFC(Microsoft Foundation Classes,微软基础类库),从而使得Windows应用程序设计的工程变得简单起来。2.1 应用程序主函数的C+
10、类封装2.1.1 窗体类的声明窗体类应该具有窗口类属性的定义、窗口类的注册、窗口的创建和显示等与窗口相关的功能。同时在类中还应该有一个HWND类型的窗口句柄hWnd,作为类的数据成员。Class CFrameWndpublic:HWND hWnd;public: int RegisterWindow(); void Create(LPCTSTR lpClassName,LPCTSTR lpWindowName); void ShowWindow(int nCmdShow); void UpateWindow();2.1.2 应用程序类的声明在应用程序类中,至少应该有一个窗口体类对象m_pMai
11、nWnd作为类的数据成员。同时这个类还有函数成员InitInstance和Run。在函数InitInstance中创建窗体对象pMainWnd,并通过它调用窗口类的成员函数来进行注册、创建、显示等初始化操作;用成员函数Run来进行消息循环。Class CWinApppublic:CframeWnd * m_pMainWnd;public: BOOL InitInstance(int nCmdshow); Int Run(); BOOL CWinApp :InitInstance(int nCmdshow); m_pMainWnd =new CFrameWnd; m_pMainWnd -Crea
12、te(NULL,”封装的Windows程序”);/创建窗口m_pMainWnd -ShowWindow(nCndShow);/显示窗口m_pMainWnd -UpdateWindow();/更新窗口显示return TRUE;int CWinApp :Run()while(GetMessage(&msg,NULL,NULL,NULL) TranlateMessage(&msg); DispatchMessage(&msg);return msg.wParam;2.1.3 主函数封装后的程序CWinApp theApp;Int APIENTRY WinMain(.) int ResultCode
13、=-1; theApp.IniInstance(cCmdShow); return ResultCode=theApp.Run(); /-窗口函数-LRESUALT CALL BACK WndPro(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)switch(message) case WM_LBUTTONDOWN: case WM_DESTROY:PostQuitMessage(0);break; default:return DefWindowro(hwnd,message,wParam,lParam);return 0;2.2 窗口
14、函数的封装消息映射2.2.1 消息处理函数的简单封装把窗口函数封装在CCmdTarget类中,而窗口类CFrameWnd则以CCmdTarget类为基类来派生。2.2.2 消息映射为了使应用程序代码更具有可读性,MFC使用了一个叫做消息映射的机制来实现消息响应函数的封装。1类的消息映射表首先,MFC要求程序员在声明可以响应消息的类时,要像声明成员函数一样,在类中声明如下形式的消息响应函数:afx_msg void 消息处理函数();然后,在每一个声明了消息响应函数的类中,定义一个数组,该数组的每个元素把消息标识和消息处理函数一一对应起来,所以这个每个元素都含有消息标识和消息处理函数名的数组就叫
15、做类的消息映射表。2消息映射表在MFC中,可以响应消息的类从CCmdTarget派生出来的一个类族。在这个类族中的每个类都可以响应消息,也就是说,类族中的每个类都有可能具有这样一个类的消息映射表。为了把类族中各个类的消息映射表连接成一个大表,要求再建立链表,从而通过这个链表把各个类的消息函数映射连接起来,形成一个大表。由于这个大表的作用就是根据消息取得消息处理函数,所以这个大表在MFC中叫做消息映射表。有了这样一个消息映射表,则无论哪个类接收到消息,都可以在接收到消息的类或其基类中找到对应的消息处理函数。2.2.3 消息映射的声明和定义根据上面的叙述,程序员在设计一个需要响应消息的类时,必须在
16、类中声明一个消息映射表,而且要在类的外部实现该消息映射表(填空相关的内容)。为了简化程序员的工作,MFC把完成上述功能的代码封装在下述4个宏中:(1)DECLARE_MESSAGE_MAP这个宏在类的声明中使用,用来声明消息映射表(2)BEGIN_MESSAGE_MAP这个宏在类声明外使用,用来实现链表和填写链表中的数据,其格式为:BEGIN_MESSAGE_MAP(类名称,基类名称)(3)ON_消息类型这个宏使用在宏BEGIN_MESSAGE_MAP()的后面,依次填写类消息映射表中的各个元素,这个宏有两种格式。 对于标准窗口消息,程序员需要把窗口消息的宏定义写在ON_后面,如:ON_WM_
17、LBUTTONDOWN() 对于菜单选项、快捷键和控件产生的Windows命令消息ON_COMMAND,在填写消息映射表时需要两个参数:ON_COMMAND(消息ID,消息处理函数名)其中,消息ID就是程序员定义的菜单选项、快捷键或控件的ID。(4)END_MESSAGE_MAP消息处理函数表的结束宏。用来添加消息处理函数表的结束标志。小结CWinApp类是MFC对Windows主函数的封装,通过派生CWinApp可以得到自己的应用程序类,在应用程序类中主要实现了全局初始化操作(如注册窗口类等),应用程序类创建了主窗口后便进入了消息循环。应用程序的主窗口一般都是CFrameWnd的派生类,可以
18、通过派生该类得到自己的主窗口类。Windows应用程序的窗口函数封装到CCmdTarget中,所有希望响应消息的类都应该以CCmdTarget为基类来派生。MFC是用消息映射表来实现消息与消息响应函数之间的映射的。MFC通过4个宏来声明和实现消息映射表。MFC的这种表驱动的机制使消息处理结构变得更加清晰、明了。第3章 MFC应用程序框架如果把设计Windows应用程序所需要的API函数与其相关的数据封装成类,便可以利用类的继承性充分实现代码的重用,并能在类的派生过程中对它进行必要的改造,从而快速地获得所需要的类,提高应用程序的开发效率。为此人们开发了MFC类库,以满足这个要求。在MFC中有一组
19、专门用来构成应用程序框架的类,用这些类可以快速创建应用程序的框架。3.1 早期的应用程序框架及其MFC类3.1.1 早期的应用程序框架早期MFC应用程序的框架由两个对象组成:应用程序类CWinApp的派生类对象和框架窗口类CFrameWnd的派生类对象,后者作为一个成员对象嵌在前者之中。图3-1描述一个早期MFC程序的典型结构。其中,CMyApp是应用程序类CWinApp的派生类;而CMyWnd是框架窗口类CFrameWnd的派生类。在主函数WinMain中,CWinApp派生类的对象theApp通过调用它的成员函数来完成程序的初始化及消息循环等一系列工作。在CWinApp的成员函数InitI
20、nstance中,CMyWnd的对象pMainWnd在调用了成员函数完成窗口的创建和显示后,被赋给CWinApp的成员m_pMainWnd形成了应用程序的主窗口。3.1.2 MFC的窗口类框架窗口类CFrameWnd是一个重要的类,它的对象通常就是应用程序的主窗口。1CObject类大多数MFC类都是从CObject中直接或间接在派生出来的。CObject给那些继承它的类型提供了3个重要的特性:串行化支持、运行时类信息支持、诊断和调试支持2CCmdTarget类为支持消息处理,MFC以CObject类为基类派生了CCmdTarget类,并在这个类中封装了窗口函数,因此凡是希望具有处理Windo
21、ws消息的功能的类都必须对CCmdTarget类为基类派生。3CWnd类Windows把应用程序界面的许多元素,例如控制栏、对话框、视图、属性页和控件等,都看作是子窗口。为了对这些窗口类提供应有的通用属性和方法,MFC以CCmdTarget类为基类派生CWnd类。所以,凡是以窗口形式为外观并且可以响应消息的类(例如,按钮类CButton、滚动条类CScrollBar类),它们的基类都是CWnd类。CWnd类的部分成员函数见表3-1。表3-1 CWnd类的部分成员函数函数说明Create( )创建一个子窗口EnableWindow( )使窗口的鼠标和键盘输入有效ModifyStyle( )改变窗
22、口的样式MoveWindow( )改变窗口的位置和大小PreCreateWindow()在程序显示窗口之前改变窗口的样式SetWindowText( )设置窗口标题的文本ShowWindow( )显示或隐藏窗口4CFrameWnd类应用程序框架窗口类CFrameWnd是由CWnd类派生的,它或它的派生类对象就是应用程序的主窗口。由于主窗口可以作为其他窗口的容器,所以它也叫做框架窗口。CFrameWnd类从基类中继承了许多成员函数。除此之外,CFrameWnd类还添加了一些程序主窗口所需要的成员函数。在设计应用程序时,程序员就是用这个类或其派生类来创建应用程序主窗口的。3.1.3 CWinApp
23、类CWinApp在MFC类族中的位置见图3-3。由图可见,CWinApp具有CObject类和CCmdTarget类的全部特性。其中,CWinThread是为了支持Windows多线程工作方式而设计的一个类。值得注意的是,MFC把原来在CWinApp类中定义的CWnd * 类的数据成员m_pMainWnd(程序的主窗口对象)放在CWinThread中定义,所以在CWinApp类声明中看不到这个对象。除此之外,CWinApp类还有3个可以重写的虚成员函数:InitApplication、InitInstance和Run。其中成员函数InitInstance是为了程序创建窗口和显示窗口所设置的。因
24、此在设计程序时,必须在CWinApp类的基础上派生出自己的应用程序类,并对函数InitInstance进行重写,以实现对窗口的不同的要求。3.2 应用程序的文档/视图结构及其框架类目前,用MFC设计的Windows应用程序几乎都采用文档/视图结构。这种程序框架与简单程序框架之间的重要区别就在于形成应用程序的主窗口不只需要一个类的对象,而是需要3个类的对象:框架窗口类CFrameWnd对象、视图类CView对象和文档类CDocument对象。框架窗口类CFrameWnd对象作为窗口的框架,视图类CView对象作为贴附在框架窗口上的用户区,二者共同形成了应用程序的界面,而文档类CDocument对
25、象则在其后专门对数据进行存储和管理。在应用程序类CWinApp中,使用文档模板把上述3个类的对象组装在一起,从而形成一个完整的程序实体。3.2.1 文档/视图结构的基本概念MFC把早期的框架窗口类的功能分解成了3个部分:数据存储管理部分、数据显示及接收用户消息部分和用来承载前部分的窗口框架部分。这3个部分分别对应了3个类:文档类CDocument、视图类CView和框架窗口类CFrameWnd。之所以这样做,就是为了把数据的存储与数据的显示分开。这样做的好处就是能够使一份文档(数据)可以用多个视图来进行显示。在文档/视图结构的框架中,视图类CView类的对象用来显示数据并接受用户操作,而框架窗
26、口类CFrameWnd是视图类对象的容器。换句话说,视图类对象是装在框架窗口类对象上的用户区。至于文档类CDocument对象的作用,则是在幕后存储和管理数据,因此文档类对象在程序界面上是看不到的。3.2.2 文档类CDocument在文档/视图结构的应用程序中,应用程序的数据是由文档类CDocument对象来存储和管理的。MFC AppWizard生成应用程序框架时,会自动以文档类CDocument为基类,为应用程序派生一个类名称中含有工程名的文档类,这个派生文档类的主要代码如下(例如工程名为My):class CMDoc:public CDcumentprotected: CMDoc( )
27、; DECLARE_DYNCREATE(CMDoc) public:virtual BOOL OnNewDocument( );virtual void Serialize(CAchive & ar);virtual CMDoc( );DECLARE_MESSAGE_MAP();在使用文档/视图结构的程序框架设计应用程序时,程序员应该在这个类中定义程序所需的数据成员及它们和外部进行联系的成员函数,而这些数据成员初始化工作则由文档类的构造函数来完成。3.2.3 视图类CView视图类CVew是窗口类CWnd的一个派生类。视图类的对象没有自己的边框,它的主要作用是为框架窗口提供用户区显示数据以及接
28、受应用程序用户对程序的操作,从而实现应用程序与用户交互。由于MFC把大多数用户消息的处理放到了CVew类中,所以这里是程序员编写代码最多的地方。它的部分代码如下:class CVew:public CWnd DECLARE_DYNAMIC(CVew) public: CDocument * GetDocument() const; virusal void OnInitialUpdate( );protected: virtual void OnDraw(CDC * pDC)=0; DECLARE_MESSAGE_MAP;视图类有几个重要的成员函数。(1)GetDocument函数从视图类的代
29、码中可以看到,GetDocument函数可以返回文档类对象的指针,因此,可以通过它来与文档类进行联系。这个函数是视图类对象获取文档数据的重要手段,在程序设计时,视图类对象就是通过它来访问文档类对象的数据的。(2)OnDraw函数这是一个消息处理函数,它的作用是更新视图的显示。这个函数有一个指向CDC类的指针参数,通过它可以直接调用CDC类的方法在视图上显示数据和图形。在应用程序窗口出现及大小发生变化时,系统将自动调用OnDraw函数。(3)OnInitialUpdate函数视图类OnInitialUpdate函数在应用程序启动或用户通过菜单新建、打开文档时被调用。因此用户可以在这个函数中作一些
30、对文档的初始化工作。在设计应用程序,一般都要自CView类派生自己的类。如果是使用MFC AppWizard来创建应用程序,向导会为程序员自动生成一个含有工程名的CView类的派生类。例如,工程名为My,则向导派生类的类名称为CMyView,这个类的主要代码为:class CMyView:public CView protected:CMyView( );DECLARE_DYNAMIC(CMyView) public: CDocument * GetDocument(); virtual void OnDraw(CDC * pDC)=0;Virtual CMyView( );protected
31、: DECLARE_MESSAGE_MAP;3.2.4 框架窗口类CFrameWnd类CFrameWnd声明的部分代码为:class CFrameWnd:public CWnd DECLARE_DYNAMIC(CMainFrame) protected: CView * m_pViewActive;/在类中定义了一个Cview类的对象 DECLARE_MESSAGE_MAP;从代码中可以看到,它定义了一个CView类的对象,除此之外,MFC把框架窗口类CFrameWnd原有的大部分功能交给了视图类,只保留了一些处理一般窗口消息的函数(如改变窗口的大小、窗口最小化等的成员函数),且这些函数的代码也由程序设计向导写好了。因此编程的时候,程序员不需要关心此类消息的处理。一般情况下,在MFC程序设计时
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1