C及Windows可视化程序设计刘振安著学习课程_第1页
C及Windows可视化程序设计刘振安著学习课程_第2页
C及Windows可视化程序设计刘振安著学习课程_第3页
C及Windows可视化程序设计刘振安著学习课程_第4页
C及Windows可视化程序设计刘振安著学习课程_第5页
已阅读5页,还剩137页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、本章将介绍如何使用MFC类库编程,并通过一个简单的例子说明MFC的消息处理机制。然后模拟文档/视结构编制一个程序,通过它引入使用向导和文档/视结构的概念,为学习下一章的文档/视结构打下基础。最后给出一个基于对话框风格的实例。第1页/共142页第一页,编辑于星期五:十五点 五十三分。在使用MFC类库编程之前,先来看一个使用全局对象的例子。【例12.1】 使用全局对象启动程序运行。/example.h文件#include using namespace std;class CMyApp int x,y,z; public:CMyApp(int a=0, int b=0, int c=0):x(a)

2、,y(b),z(c) coutBegin.endl; 12.1 一个使用全局对象的程序第2页/共142页第二页,编辑于星期五:十五点 五十三分。void SetNum(int a, int b, int c) x=a;y=b;z=c;int Add() return x+y+z;/example.cpp文件#include example.hCMyApp theApp; /全局对象void main()coutIn main.endl;theApp.SetNum(2,4,6);couttheApp.Add();coutendlGo out.endl;第3页/共142页第三页,编辑于星期五:十五

3、点 五十三分。程序运行输出如下: Begin. In main. 12 Go out.程序是先执行“CMyApp theApp;”创建并初始化惟一全局对象theApp,输出信息“Begin.”,然后进入主程序。Visual C +6.0的程序设计方法也是如此,即先建立并初始化惟一的全局对象theApp。图12.1左边的框中给出这个程序的类图及全局变量图,右边是程序的.cpp文件。第4页/共142页第四页,编辑于星期五:十五点 五十三分。图12.1 Object的类图及全局变量第5页/共142页第五页,编辑于星期五:十五点 五十三分。C +类库运用面向对象技术,大大简化了程序的设计工作。Micr

4、osoft公司提供的基础类库MFC(Microsoft Foundation Classes)就是一个常被称为Application Framework的大类库,其中包含用来开发Windows下C +应用程序的一组类,封装了大部分的Windows API,大大加速Windows下C/C +程序员的软件设计。12.2 使用MFC编制Win32 Application程序第6页/共142页第六页,编辑于星期五:十五点 五十三分。习惯上分别用Win16 和Win32区别16位和32位Windows程序,Windows API则是泛指两者。由前面的讨论可知,驾驭数以千计的API函数并非易事。MFC则把

5、这些浩繁的API函数逻辑地组织起来,使它们具有面向对象的抽象性、封装性、继承性和多态性等特点。怎样学习MFC类库,各人意见不一。很多人认为: “学习MFC,最重要的一点是要学会抽象地把握问题,不求甚解”。这可能对减轻学习难度、提高兴趣是有帮助的,不过也有一些问题,即很多人成了代码拼凑机、只见树木不见森林。第7页/共142页第七页,编辑于星期五:十五点 五十三分。其实,在学习开始就了解Windows程序的基本运行原理,并了解MFC是怎样与之结合的,能大大加快理解,让人更易于接受MFC,这样学习似慢实快。相反,很多书从头到尾地教读者如何如何做,最后仍旧不知所以。学习MFC要理解MFC的应用程序框架

6、,熟记其类层次结构,并不需要刻意去记忆众多的类及它们的成员函数。第8页/共142页第八页,编辑于星期五:十五点 五十三分。【例12.2】使用MFC类库编制输出“Hello MFC”的程序。文档/视的结构比较复杂,先不让向导(AppWizard)生成代码,仍然用Visual C +6.0集成开发环境生成一个空的Win32应用程序项目SMFC1。建立SMFC1.cpp文件如下:第9页/共142页第九页,编辑于星期五:十五点 五十三分。#include /使用MFC类库需要包含的头文件class CMyWin:public CFrameWnd public:CMyWin() protected:af

7、x_msg void OnPaint();DECLARE_MESSAGE_MAP() /声明宏;/使用消息宏BEGIN_MESSAGE_MAP(CMyWin, CFrameWnd) ON_WM_PAINT()END_MESSAGE_MAP()第10页/共142页第十页,编辑于星期五:十五点 五十三分。void CMyWinOnPaint()CString str=Hello,MFC!;/设置输出窗口的 /字符串内容CRect rect(240,100,340,200);CPaintDC dc(this);dc.DrawText(str,&rect,DT_CENTER);class CM

8、yApp:public CWinApp public:BOOL InitInstance();第11页/共142页第十一页,编辑于星期五:十五点 五十三分。BOOL CMyAppInitInstance()/改写CWinApp的 /InitInstance函数CMyWin *pFrame=new CMyWin;pFrame-Create(0, _T(使用MFC的Win32 Application);/标题条pFrame-ShowWindow(1); /m_nCmdShow=1pFrame-UpdateWindow();this-m_pMainWnd=pFrame;return TRUE;CMy

9、App theApp;/全局对象第12页/共142页第十二页,编辑于星期五:十五点 五十三分。程序在编译前还要设置参数,使它能使用MFC类库。选择“Project”菜单的“Settings”项,弹出如图12.2所示的“Project Settings”对话框,在Microsoft Foundation Classes列表框中选择使用MFC。图12.3是编译运行的示意图,图12.4是类的组成图。这个程序比用Win32 API函数写的要简短一些,而且随着程序规模的增大,会很快显示出它的优点,编程量也会大大减小。第13页/共142页第十三页,编辑于星期五:十五点 五十三分。图12.2 设置使用MFC

10、示意图第14页/共142页第十四页,编辑于星期五:十五点 五十三分。图12.3 运行示意图第15页/共142页第十五页,编辑于星期五:十五点 五十三分。图12.4 类的组成图和全局变量第16页/共142页第十六页,编辑于星期五:十五点 五十三分。这个程序声明了两个类: 由应用程序类CWinApp派生而来的CMyApp类和由框架窗口类CFrameWnd派生而来的CMyWin类。这两个类在MFC中的地位如图12.5所示。如果写过Win32 API程序,应该能分析出上面程序的来龙去脉。第17页/共142页第十七页,编辑于星期五:十五点 五十三分。图12.5 CWinApp类和CFrameWnd类在M

11、FC中的地位示意图第18页/共142页第十八页,编辑于星期五:十五点 五十三分。Win32程序中有WinMain和WndProc函数。对于MFC程序来说,这两个函数在哪里?程序如何工作呢?1. 全局对象WinMain和WndProc函数被封装在类库里,从而免去用户手写的麻烦。上面用到的CWinApp代表程序体,CFrameWnd则代表一个主框架窗口。MFC的基类名均以字母C开头,习惯上为使用MFC编写的应用程序中的类起名时也是这样做。简单分析第19页/共142页第十九页,编辑于星期五:十五点 五十三分。【例12.1】演示了程序使用theApp全局对象。对比图12.1和图12.4的类结构图,它们

12、都有一个全局对象theApp。main在图12.1中以全局函数身份出现,WinMain没有像main那样出现在图12.4中。简单说来,是MFC应用程序将它封装起来,只展现出theApp全局对象。2. WinMain函数寻迹Windows 操作系统在初始化该应用程序进程的同时,将自动为该应用程序创建一个主线程,并将控制权交与C/C +运行时(Runtime)库的启动函数。注意程序中定义了一个全局对象: CMyApp theApp;第20页/共142页第二十页,编辑于星期五:十五点 五十三分。此时,其构造函数会被调用,初始化对象的数据成员,随后程序就进入主函数入口了。为了简少程序员的工作量。将原来

13、的大量Windows API分门别类、有组织地进行封装,主函数和窗口函数也不例外。如【例12.2】所示,程序员现在只要从MFC类派生相应的类并产生一个全局对象即可。WinMain所在的位置可参见MFC源码的appmodule.cpp中的如下代码段:extern C int WINAPI_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,第21页/共142页第二十一页,编辑于星期五:十五点 五十三分。 LPTSTR lpCmdLine, int nCmdShow)/ call shared/exported WinMainreturn A

14、fxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);“_t”是为了支持Unicode而准备的一个宏。MFC已为用户准备好这个有相当程度固定行为的函数,它仅仅调用了AfxWinMain。AfxWinMain又做了什么?下面是简化的行为:第22页/共142页第二十二页,编辑于星期五:十五点 五十三分。int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)/ AfxGetThread()返回指向当前

15、线程的指针CWinThread* pThread = AfxGetThread();/AfxGetApp()获得全局对象theApp /pApp指向全局对象theAppCWinApp* pApp = AfxGetApp(); AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);第23页/共142页第二十三页,编辑于星期五:十五点 五十三分。/CMyApp继承CwinApp虚函数InitApplication/调用CWinApp InitApplication() pApp-InitApplication();/ CMyApp改写C

16、winApp的 /虚函数 InitApplication /调用CMyApp InitApplication()pThread-InitInstance(); pThread-Run();/进入消息循环 AfxWinTerm(); return nReturnCode;第24页/共142页第二十四页,编辑于星期五:十五点 五十三分。AfxGetApp是一个全局函数,返回应用程序对象的指针。这里是取得指向CMyApp类的全局对象theApp的指针。CMyApp继承CWinApp,CMyAppInitInstance()被AfxWinMain间接调用到,于是就创建了应用程序窗口。在第11章解释过如

17、下两个函数: MyRegisterClass(hInstance); /注册窗口 InitInstance (hInstance, nCmdShow) /初始化窗口 如将MyRegisterClass(hInstance)改名为 InitApplication(hInstance),则与InitInstance函数一起,正好成了现在MFC的CWinApp的两个虚函数。第25页/共142页第二十五页,编辑于星期五:十五点 五十三分。InitApplication负责为每一个程序只注册一次窗口类,而InitInstance则为每一个例程都做一次初始化。所以,CWinApp的派生类CMyApp不需要

18、改写InitApplication虚函数,但需要改写InitInstance虚函数,并在其中把窗口产生出来。调用由CWinApp类继承的Run()成员函数进入消息循环。程序结束时则调用CWinApp的ExitInstance()函数退出。既然WinMain的困惑没有了,WndProc也一样,读者若有兴趣可读一读MFC源码。第26页/共142页第二十六页,编辑于星期五:十五点 五十三分。3. 消息循环在注册了窗口类和创建了窗口之后,就要靠消息循环了。这就该CWinThreadRun()表演了。int CWinThreadRun() . if (!PumpMessage() return Exit

19、Instance(); .下面是CWinThreadPumpMessage()的代码:第27页/共142页第二十七页,编辑于星期五:十五点 五十三分。BOOL CWinThreadPumpMessage() if (!GetMessage(&m_msgCur, NULL, NULL, NULL) return FALSE; if (m_msgCur.message != WM_KICKIDLE & !PreTranslateMessage(&m_msgCur) TranslateMessage(&m_msgCur);DispatchMessage(&m_

20、msgCur); return TRUE;第28页/共142页第二十八页,编辑于星期五:十五点 五十三分。原来熟悉的消息循环在此。用C写Win32程序时,处理消息是在窗口子过程中的巨大而复杂的switch/case中,而MFC采用消息映射机制来决定如何处理特定的消息。这种消息映射机制包括一组宏、映射成员函数等。剖析相关的宏不是本教材的任务,故这里仅介绍手工编写的方法。虽然以后是用向导而不必手工输入,但初学时还是知道一点原理为好。本程序需要在处理WM_PAINT消息时输出一字符串,就以此为例简单说明消息宏的使用。首先在窗口类(即要处理消息的类,这里是CMyWin)的定义中加入宏:第29页/共14

21、2页第二十九页,编辑于星期五:十五点 五十三分。 DECLARE_MESSAGE_MAP();然后在实现文件中使用一对由 BEGIN_MESSAGE_MAP()开始, END_MESSAGE_MAP()结束的程序体。BEGIN_MESSAGE_MAP()的两个参数分别为处理消息的类及其基类,此两宏建造了一个类似于11.3节所述的大表格;接着制造表中每项,这由ON_.宏完成,它们都是标准的,由MFC定义。例如要处理WM_PAINT消息,就应该用ON_WM_PAINT()宏,用户处理程序默认的是“类名OnPaint()”函数。下面是这个程序的源码:第30页/共142页第三十页,编辑于星期五:十五点

22、 五十三分。class CMyWin:public CFrameWnd public:CMyWin() protected:afx_msg void OnPaint();/声明消息处理函数DECLARE_MESSAGE_MAP()/声明宏;/ CFrameWnd是CMyWin的基类BEGIN_MESSAGE_MAP(CMyWin, CFrameWnd)ON_WM_PAINT()/对应CMyWinOnPaint()END_MESSAGE_MAP()第31页/共142页第三十一页,编辑于星期五:十五点 五十三分。从程序中可以看出,具有消息的函数用afx_msg 标识。如图12.5所示,处理消息的基

23、类是CCmdTarget,各个派生类继承这个消息处理机制。回忆11.3节用数组实现的消息处理,很容易发现MFC使用的是同一原理,只是设计了功能强大的宏帮助实现。4. 程序结构总结从程序中可以看出,由于MFC已经把Windows API都封装起来了,程序代码已经没有原来熟悉的结构了。简单说来,看不到WinMain,不知道程序从何处开始执行;看不到如何注册窗口类;看不到消息循环和窗口处理函数。第32页/共142页第三十二页,编辑于星期五:十五点 五十三分。其实,它现在变得很简单。下面假设程序运行处理WM_PAINT消息,可分为准备、运行和结束阶段,分析各个阶段的过程。(1) 程序的准备阶段程序的准

24、备工作可分为如下几个过程: theApp是应用程序的全局对象,代表整个程序, 其基类是CWinApp,产生theApp,配置内存并建 立初值; 函数AfxWinMain通过AfxWinIni函数调用 AfxInitThread函数设置消息队列; AfxWinMain调用Application函数,它是 CWinApp的虚函数,AfxWinMain一般只是调 用而不改写它;第33页/共142页第三十三页,编辑于星期五:十五点 五十三分。 AfxWinMain调用InitInstance函数,它是 CWinApp的空虚函数,必须重新定义它; 定义CWinApp派生类CMyApp的空虚函数 CMyA

25、pp InitInstance()。 框架对象代表主窗口,其基类是CFrameWnd, CMyWin *pFrame=new CMyWin; 语句使用“new”创建一个CMyWin 的对象,程序中 将CMyWin的构造函数设计为空函数; 使用函数Create产生主窗口,然后写入标题条内容; 执行函数ShowWindow显示窗口; 执行函数UpdateWindow,发出WM_PAINT消息; 执行函数Run,进入消息循环。第34页/共142页第三十四页,编辑于星期五:十五点 五十三分。(2) 程序的运行阶段程序运行的过程如下: 程序由CwinAppRun()中的GetMessage() 循环获得

26、WM_PAINT消息; WM_PAINT经由DispatchMessage()送到窗口 函数CWndDefWindowProc()中; CWndDefWindowProc()将消息传递并通过 消息映射表格(Message Map);在传递过程中发现有相符合的项目,就调用项 目中对应的函数,此函数是应用程序利用 BEGIN_MESSAGE_MAP和 END_MESSAGE_MAP之间的宏设立起来的;第35页/共142页第三十五页,编辑于星期五:十五点 五十三分。系统提供处理标准消息的标准命名方法,例如 WM_PAINT由函数OnPaint处理,这里的程序也 将消息处理函数声明为afx_msg v

27、oid OnPaint()。(3) 程序的结束阶段程序结束的过程为: 本程序的CMyWin没有处理WM_CLOSE消息, 如果使用“文件”菜单的“关闭”命令,则交给默认 的处理程序,默认函数调用DestroyWindow() 并发出WM_DESTROY消息;默认的WM_DESTROY处理方式是调用 PostQuitMessage(),发出WM_QUIT; CWinAppRun()接收WM_OUIT,结束消息 循环;第36页/共142页第三十六页,编辑于星期五:十五点 五十三分。然后调用CWinApp的虚函数ExitInstrance,但 最终是执行CWinAppExitInstrance(),

28、还是CMyAppExitInstrance(),则决定于CMyApp是否改写了ExitInstrance 函数; 最后执行函数AfxWinTerm结束程序。由此可见,我们主要是产生两个派生类。CWinApp类提供的4个关键虚成员函数如下: virtual BOOL InitAppLication( );/构建窗口 virtual BOOL InitInstance( )/与窗口有关的空函数 virtual in Run( );/处理消息 virtual intExitInstance( );/结束运行注意: 派生类一定要改写虚函数InitInstance。第37页/共142页第三十七页,编辑于

29、星期五:十五点 五十三分。AppWizard生成的MFC应用程序(非基于对话框的),都建立在文档/视结构之上。所以,在使用AppWizard之前,有必要简介一下此结构。本节通过手工编制多个文件来模拟一个最简单的单文档程序。仍然使用以前的方法,创建一个名为Smfc2的空Win32工程,12.3 模拟文档/视结构的MFC程序第38页/共142页第三十八页,编辑于星期五:十五点 五十三分。手工建立4个.cpp文件: MainFrame.cpp Smfc2.cpp Doc.cpp View.cpp5个头文件: Doc.h MainFrame.h View.h Smfc2.h resource.h1个资

30、源文件SMFC2.rc等共10个文件。图12.6给出在集成环境中使用FileView观察到的文件结构图,而图12.7则是使用ClassView观察到的类结构图和全局对象theApp。第39页/共142页第三十九页,编辑于星期五:十五点 五十三分。图12.6 文件结构图第40页/共142页第四十页,编辑于星期五:十五点 五十三分。图12.7 类结构图和全局对象theApp第41页/共142页第四十一页,编辑于星期五:十五点 五十三分。可以直接使用文本编辑器编辑这些文件,然后按图12.6的结构装入项目中,也可以直接在项目中按此图建立文件并输入相应内容。下面不是集中给出.cpp文件,而是先给出头文件

31、,接着给出相应的.cpp文件,这样做的目的是容易对比。第42页/共142页第四十二页,编辑于星期五:十五点 五十三分。/ Smfc2.hclass CMyApp:public CWinApppublic:BOOL InitInstance();/声明改写虚函数InitInstanceDECLARE_MESSAGE_MAP()/声明消息映射(没有 /声明消息处理函数);程序清单第43页/共142页第四十三页,编辑于星期五:十五点 五十三分。/Smfc2.cpp:#include Doc.h#include MainFrame.h#include View.h#include Smfc2.h#in

32、clude resource.hBEGIN_MESSAGE_MAP(CMyApp, CWinApp)END_MESSAGE_MAP()/因为没有声明消息处理 /函数,所以没有实现细节BOOL CMyAppInitInstance()/改写虚函数 CSingleDocTemplate* pDocTemplate;/建立单文档模板第44页/共142页第四十四页,编辑于星期五:十五点 五十三分。 pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CMyDoc),RUNTIME_CLASS(CMainFrame),RUN

33、TIME_CLASS(CMyView) ); AddDocTemplate(pDocTemplate); CCommandLineInfo cmdInfo; /处理命令行 ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo) return FALSE; m_pMainWnd-ShowWindow(SW_SHOW); m_pMainWnd-UpdateWindow(); return TRUE;第45页/共142页第四十五页,编辑于星期五:十五点 五十三分。CMyApp theApp;/声明全局对象/MainFrame.h:#i

34、nclude class CMainFrame : public CFrameWnd protected:DECLARE_DYNCREATE(CMainFrame)afx_msg void OnAppExit(); /声明消息处理函数DECLARE_MESSAGE_MAP() /声明消息映射;第46页/共142页第四十六页,编辑于星期五:十五点 五十三分。/MainFrame.cpp:#include MainFrame.h#include resource.hIMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)/开始实现宏BEGIN_MESSAGE_MAP(C

35、MainFrame, CFrameWnd)ON_COMMAND(ID_EXIT, OnAppExit)/实现消息映射表END_MESSAGE_MAP() /结束宏void CMainFrameOnAppExit() this-OnClose();/Doc.h:#include 第47页/共142页第四十七页,编辑于星期五:十五点 五十三分。class CMyDoc : public CDocumentchar str11; protected:DECLARE_DYNCREATE(CMyDoc) public:char *GetStr();CMyDoc()strcpy(str,Hello,MFC

36、!);virtual void Serialize(CArchive& ar); protected:/没有消息处理函数DECLARE_MESSAGE_MAP() ;/Doc.cpp:#includeDoc.h第48页/共142页第四十八页,编辑于星期五:十五点 五十三分。IMPLEMENT_DYNCREATE(CMyDoc, CDocument)BEGIN_MESSAGE_MAP(CMyDoc, CDocument) /没有消 /息处理函数END_MESSAGE_MAP()void CMyDocSerialize(CArchive& ar)if (ar.IsStoring()

37、elsechar *CMyDocGetStr()return str;第49页/共142页第四十九页,编辑于星期五:十五点 五十三分。/View.h:#include class CMyDoc;class CMyView : public CView protected: DECLARE_DYNCREATE(CMyView) public:CMyDoc* GetDocument();virtual void OnDraw(CDC* pDC); protected:DECLARE_MESSAGE_MAP();第50页/共142页第五十页,编辑于星期五:十五点 五十三分。inline CMyDoc

38、* CMyViewGetDocument() return (CMyDoc*)m_pDocument; /View.cpp:#include View.h#include Doc.hIMPLEMENT_DYNCREATE(CMyView, CView)BEGIN_MESSAGE_MAP(CMyView, CView)END_MESSAGE_MAP()void CMyViewOnDraw(CDC* pDC) CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDC-TextOut(250,150,pDoc-GetStr();第51页/共142页第

39、五十一页,编辑于星期五:十五点 五十三分。/resource.h:#define IDR_MAINFRAME1#define ID_EXIT40001/资源文件SMFC2.rc:#include resource.hIDR_MAINFRAME MENU DISCARDABLE BEGIN POPUP 文件 BEGINMENUITEM 退出,ID_EXIT ENDEND第52页/共142页第五十二页,编辑于星期五:十五点 五十三分。STRINGTABLE DISCARDABLE BEGINIDR_MAINFRAME mfc_singlennMfc_sinn nMfcsingle.Document

40、nMfc_si DocumentEND将使用MFC的方式设置为 Use MFC in a Shared DLL编译运行如图12.8所示。第53页/共142页第五十三页,编辑于星期五:十五点 五十三分。图12.8 模拟文档/视/框架结构程序运行示意图第54页/共142页第五十四页,编辑于星期五:十五点 五十三分。Document/View(文档/视)互为表里。View本身就是一个窗口,但其外围必须再加上一个框架窗口(这里为CMainFrame)作容器,它是数据的“面”;Document则是数据的“体”。使用文档/视就是要把数据与它的表现分开,让Document专心管理数据,让View专门显示数据

41、。CView要提供一个GetDocument成员函数来获取与视相关的文档的指针,通过该指针可以存取文件文档中的数据。下面对模拟程序进行简要说明。第55页/共142页第五十五页,编辑于星期五:十五点 五十三分。1. 声明消息处理函数和消息映射宏假设一个广义的基类为Base,派生类为Derived(所谓广义,就是以后可以将它们与实际的对应)。例如CwinApp作为基类,自己设计的CMyApp为派生类;CView作为基类,CMyView为派生类;CDocument作为基类,CMyDoc为派生类等。为了介绍简单方便,还假设消息处理函数没有参数,其他与演示消息映射无关的部分均省略。下面是假设有两个处理消

42、息的成员函数的声明:多文件中的消息映射第56页/共142页第五十六页,编辑于星期五:十五点 五十三分。class Derived:public:Baseprotected: afx_msg void OnFirstName();/声明消息处理函数 DECLARE_MESSAGE_MAP()/声明消息映射;假设这里声明的成员函数是用来处理Windows的COMMAND消息的成员函数,则它们必须遵循两个游戏规则: 以afx_msg void为消息处理函数的类型,函数的名字必须以On开始,即形如 afx_msg void OnFirstName的形式。FirstName是假设的字符串,一般与映射表定

43、义有内在联系,详见下节。第57页/共142页第五十七页,编辑于星期五:十五点 五十三分。接着使用一个宏声明 DECLARE_MESSAGE_MAP()它声明的是一个数据结构,这个数据结构有着规定的格式。其实,它们就是第11.3节演示的结构数组。2. 实现消息映射声明消息映射数据结构之后,还需要为这个数据结构填入消息映射的数据。像【例12.2】那样简单的程序,声明和实现是在同一个.cpp文件里。但对现在的多文件来说,是将声明部分放在类Derived的头文件中,实现放在它的.cpp文件中。必须按照DECLARE_MESSAGE_MAP()声明的数据结构格式填写,这由3部分来完成。第58页/共142

44、页第五十八页,编辑于星期五:十五点 五十三分。第1部分是开始宏。它的格式为: BEGIN_MESSAGE_MAP(Derived, Base)这个宏有两个参数,一个是消息处理函数所属的派生类的类名Derived,一个是Drived的直接基类的类名Base。第2部分由一系列对应的消息宏填写。这相当于填写结构数组的数据,而且填写方式简单。假设处理的是Windows的命令消息,则使用 ON_COMMAND宏这个宏有两个参数,一般的命令消息(WM_COMMAND)的对应规律如下: ON_COMMAND(id, OnFirstName)第59页/共142页第五十九页,编辑于星期五:十五点 五十三分。 O

45、nFirstName 声明的消息处理函数名 FirstName 如果它用First和Name两个字符 串组成,则大写每一个字符串的 第1个字母,这样做一般是为了 更好地表达函数的功能,例如 FileNew和FileSave等。它们与id的选择有功能意义上的关联。对菜单命令来说,id就是菜单标识符,一般常见如下对应规律: ON_COMMAND(IDM_FirstName, OnFirstName)ON_COMMAND(IDM_Name, OnName)ON_COMMAND(IDM_Name, OnFirstName)下面是系统默认的几个例子: ON_COMMAND(IDM_FILENEW,OnF

46、ileNew)第60页/共142页第六十页,编辑于星期五:十五点 五十三分。ON_COMMAND(IDM_FILEOPEN,OnFileOpen)ON_COMMAND(IDM_FILESAVE,OnFileSave)ON_COMMAND(IDM_ABOUT,OnAbout)ON_COMMAND(ID_ EXIT,OnAppExit)第3部分是结束宏,格式为: END_MESSAGE_MAP()如果这个文件里没有要处理的消息,则有两种处理的方法: 头文件里声明宏,即:class Derived:public:Base protected: DECLARE_MESSAGE_MAP();第61页/共

47、142页第六十一页,编辑于星期五:十五点 五十三分。在实现文件里给出开始宏和结束宏,即:BEGIN_MESSAGE_MAP(Derived, Base)END_MESSAGE_MAP()注意宏的实现不能放在实现文件中的函数中,它们是单独的结构,不属于任何函数。 将它们同时省略。注意不要只省略头文件中的声明或.cpp文件中的实现。为了加深理解,本程序分别对以上两种方法进行了演示。3. 实现消息处理在实现文件中编制消息处理函数。例如对于OnFirstName 函数,则有: 第62页/共142页第六十二页,编辑于星期五:十五点 五十三分。void OnFirstName( )/函数体定义结论: 在头

48、文件里声明派生类,在类中声明消息处理函数之后,声明消息处理宏。在派生类的实现文件中单独实现宏。4. 使用实例本小节的例子中只有一个文件需要消息处理,对于菜单项Exit来说,首先在MainFrame.h中声明如下: /MainFrame.h第63页/共142页第六十三页,编辑于星期五:十五点 五十三分。class CMainFrame : public CFrameWnd/派生关系 protected:DECLARE_DYNCREATE(CMainFrame)afx_msg void OnAppExit();/消息处理函数DECLARE_MESSAGE_MAP()/声明宏;下面是在MainFra

49、me.cpp中的实现:/MainFrame.cppBEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)ON_COMMAND(ID_EXIT, OnAppExit)第64页/共142页第六十四页,编辑于星期五:十五点 五十三分。END_MESSAGE_MAP()注意: 这里菜单标识符ID_EXIT应与它在资源文件中定义的一样。OnAppExit消息处理函数在程序中的实现如下:void CMainFrameOnAppExit() this-OnClose();第65页/共142页第六十五页,编辑于星期五:十五点 五十三分。资源文件是一种以后缀为.rc的、用文字来描述资源

50、的文件。常用的资源很多,而且随着发展还会有新的资源不断加入。常用的资源有: ACCELERATOR ICON CURSO BITBMP FONT DIALOG VERSIONINFO TOOLBAR MENU使用这些关键字定义资源时,是把被定义的名字放在前面。下面给出定义菜单的一般格式:菜单名MENU描述(可选项)BEGIN/开始定义 POPUP弹出菜单名/菜单条上的名字资源文件第66页/共142页第六十六页,编辑于星期五:十五点 五十三分。 BEGIN /菜单命令MENUITEM菜单命令名,菜单标识符/继续定义其他菜单命令 END POPUP/继续定义BEGIN/继续定义END/继续定义EN

51、D/结束定义第67页/共142页第六十七页,编辑于星期五:十五点 五十三分。菜单体由BEGIN开始,由END结束(也可使用一对“ ”号)。菜单条的每一个弹出菜单由POPUP指定,在一对BEGIN和END中,使用MENUITEM定义它的菜单命令。一般可以交替使用BEGINEND和“ ”来区分层次。下面是SMFC2.rc文件:IDR_MAINFRAME MENU DISCARDABLE BEGINPOPUP文件 BEGIN MENUITEM退出,ID_EXITENDEND第68页/共142页第六十八页,编辑于星期五:十五点 五十三分。STRINGTABLE DISCARDABLE BEGIN ID

52、R_MAINFRAME mfc_singlennMfc_sin nnMfcsingle.DocumentnMfc_siDocumentENDIDR_MAINFRAME是一个资源标识符ID,表示这一文件类型所使用的资源。在.rc文件中代表多种资源,不同类型的资源可以使用相同的ID。上面两种资源都使用这个ID,因为MENU定义了IDR_MAINFRAME,所以SRINGTABLE直接使用。ID_EXIT 是菜单命令的标识符,标识符应该是一个具体的惟一整数值,它们在resource.h文件中定义。第69页/共142页第六十九页,编辑于星期五:十五点 五十三分。/resource.h#define I

53、DR_MAINFRAME 1#define ID_EXIT 40001这些标识符已经具有惟一整数值,就可以在其他地方引用它们。可以使用Visual C + 6.0 可视化生成资源,非常方便。第70页/共142页第七十页,编辑于星期五:十五点 五十三分。模拟程序使用了文档模板(Document Template)。它有单文档模板和多文档模板之分,模拟程序使用单文档模板。程序中模拟Document/View概念,CMyView本身虽然已经是一个窗口,但其外围还必须封装一个作为舞台的外框窗口,这由CMainFrame提供。还要设计一个存放的容器,以便MyView能随时提取,这由CMyDoc实现。这三

54、者是一体的,也就是说,程序打开单文档,应产生Document/View/Frame对象。这3个对象由Document Template管理。这里是单文档,所以由CSingleDocTemplate管理。单文档模板第71页/共142页第七十一页,编辑于星期五:十五点 五十三分。应用程序必须重定义自己的虚函数InitInstance。下面是它的定义: BOOL CMyAppInitInstance()CSingleDocTemplate* pDocTemplate;pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS

55、(CMyDoc), RUNTIME_CLASS(CMainFrame), RUNTIME_CLASS(CMyView) ); AddDocTemplate(pDocTemplate);第72页/共142页第七十二页,编辑于星期五:十五点 五十三分。程序利用CsingleDocTemplate的构造函数动态建立一个CSingleDocTemplate对象。它的构造函数有4个参数:CSingleDocTemplate:CSingleDocTemplate( UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass,

56、 CRuntimeClass* pViewClass ); nIDResource是一个资源标识符ID,表示这一文 件类型所使用的资源。在.rc文件中代表多种资源, 不同类型的资源可以使用同一个ID。第73页/共142页第七十三页,编辑于星期五:十五点 五十三分。 本程序在 resource.h中定义的资源标识符ID是 IDR_MAINFRAME 它的定义见SMFC2.rc。因此,这个构造函数的 第1个参数是IDR_MAINFRAME。 pDocClass是指针,指向由CDocument派生的 CMyDoc类的CRuntimeClass对象。 pFrameClass是指针,指向由CFrameW

57、nd派生 的CMainFrame类的CRuntimeClass对象。 pViewClass是指针,指向由CView派生的 CMyView类的CRuntimeClass对象。第74页/共142页第七十四页,编辑于星期五:十五点 五十三分。文档模板接受了种类的CRuntimeClass指针,通过AddDocTemplate函数将创建好的模板加入到应用程序的可用文档模板链表中。接着还要创建及处理命令行信息。如果用命令行方式运行应用程序,则可以使用命令行参数,通常为文档文件名。如果不使用命令行参数,则应用程序会自动打开该文档。这部分的代码如下: CCommandLineInfo cmdInfo; Pa

58、rseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo) return FALSE;不过,文档模板要在运行时动态创建这3种类的对象,这就要求程序中派生的CMyDoc、 CMyView和CFrameWnd类能支持动态创建。第75页/共142页第七十五页,编辑于星期五:十五点 五十三分。程序需要解决CMyDoc、CFrameWnd和CMyView类的动态创建(Dynamic Creation)问题。这通过在头文件类的声明中用宏声明它为动态创建,在实现文件里首先用宏实现它。假设类Base派生类Derived,一般格式为: DECLARE_D

59、YNCREATE(Derived)/头文件声明IMPLEMENT_DYNCREATE(Derived,Base) / .cpp 文件实现下面以CView为例,使用方法如下: 动态创建第76页/共142页第七十六页,编辑于星期五:十五点 五十三分。/View.hclass CMyView : public CView protected: DECLARE_DYNCREATE(CMyView)/在类中 /使用 public:CMyDoc* GetDocument();virtual void OnDraw(CDC* pDC);/View.cpp:/在类外面实现IMPLEMENT_DYNCREATE

60、(CMyView, CView)第77页/共142页第七十七页,编辑于星期五:十五点 五十三分。它与消息映射宏的使用方法一样,下面以CMainFrame类为例,将两者都完整表示出来以便对照/MainFrame.h:#include class CMainFrame : public CFrameWnd/声明类 protected:DECLARE_DYNCREATE(CMainFrame)/声 /明动态创建afx_msg void OnAppExit();/消息处理函数DECLARE_MESSAGE_MAP()/声明消息映射;第78页/共142页第七十八页,编辑于星期五:十五点 五十三分。/MainFrame.cpp:#include MainFram

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论