630 likes | 836 Views
Visual C++ 与面向对象程序设计教程. 第 10 章 WINDOWS 编程. 教学目标. WINDOWS 编程的基本思想 MFC 程序的基本结构 消息映射. 学习要求. 理解 WINDOWS 的基本编程思想 了解消息传递机制 了解 WINDOWS API 了解 MFC 程序的组成及各部分的作用 掌握在视图中输出信息 掌握消息处理函数的编制. 授 课 内 容. 10.1 WINDOWS 编程的基本思想 10.2 WINDOWS API 和 MFC 编程 10.3 单文档界面( SDI )应用程序 10.4 在窗口的客户区输出文字和图形
E N D
Visual C++与面向对象程序设计教程 第10章 WINDOWS编程
教学目标 • WINDOWS编程的基本思想 • MFC程序的基本结构 • 消息映射
学习要求 • 理解WINDOWS的基本编程思想 • 了解消息传递机制 • 了解WINDOWS API • 了解MFC程序的组成及各部分的作用 • 掌握在视图中输出信息 • 掌握消息处理函数的编制
授 课 内 容 • 10.1 WINDOWS编程的基本思想 • 10.2 WINDOWS API 和MFC编程 • 10.3 单文档界面(SDI)应用程序 • 10.4 在窗口的客户区输出文字和图形 • 10.5编制消息处理函数 • 调试技术 • 程序设计举例
10.1 WINDOWS编程的基本思想 • 所有的Windows应用程序都是消息驱动的; • 消息处理是windows应用程序的核心; • 消息就是操作系统通知应用程序某件事情已经发生的一种方式; • 当用户键入、移动鼠标或双击鼠标,或者用户改变窗口的大小,都将向适当的窗口发送消息 • 一个窗口可以向另一个窗口发送消息,并且大多数窗口通过向其他窗口发送一个稍有不向的消息来响应消息。 • 与所有的Windows应用程序一样,MFC应用程序也要处理消息
消息的种类 • 在windows系统中,大约有近千种消息。 • 消息大致可以分为3类, (1)标准的widows消息 键盘消息、鼠标消息、窗口消息。 (2)控件消息 从控件传给系统的消息(BN_CLICK) 由系统发送给控件的消息(……) (3)命令消息 菜单项、工具栏按钮、加速键等用户界面对象发送的WM_ COMMAND(ID_FILE_OPEN)
消息的发送和接收 事件驱动原理
10.2 WINDOWS API 10.2 WINDOWS API Windows API • Windows 95/98和Windows NT都是基于Windows应用程序编程接口 • (API,Application Programming Interface), • 通常称为Win API。Windows 95/98和WindowsNT都使用32位的版本, 称为Win32 API,或Win32。
10.2 WINDOWS API 10.2 WINDOWS API Win32、 Windows .h 和 SDK API是大量函数加上数字常量、宏、结构、类型以及其他相关项的集合。可以从C++、Micmsoft Visual Basic、汇编语言、Fortran、Pascal以及其他编程语言中调用这些函数。 大多数 API 定义在称为Windows.h的文件中。visual C++6.0编程环境中有这个文件。也可以从Windows平台软件开发工具包(SDK,Software Development Kit)中得到它,但是如果没有特殊的要求,不需要单独的SDK。
10.2 WINDOWS API 10.2 WINDOWS API 为不同的Win32平台编程 不同的32位操作系统共用相同的API,意味着所有32位Windows的程序都将具有极高的可移植性。因此,能为Windws 95编写程序,就能为Windows 98 Windows 2000, XP和Windows NT编写程序,尤其在利用MFC时. 例: 用Windows API 编写“Hello World!” 程序
10.2 WINDOWS API 10.2 WINDOWS API WinMain 函数:DOS下的C/C++程序必须有且仅有一个main函数,在Windows中的程序必须有一个WinMain函数,WinMain函数是应用程序的入口。 窗口的创建:窗口的创建通常分以下几步,首先定义一个窗口类变量(此处的类并非是C+十中的类,而是一个结构体),填充窗口类的一些相关属性,例如:窗口的背景色、所用图标、消息处理函数等;下面用RegisterClassEx或RcgistcrClass 函数向系统注册此类;接着用CreateWindow 函数创建窗口;最后调用ShowWindow来显示窗口。但对于Windows提供的一些预设的窗口类可以无需注册而直按调用CreateWindow 函数创建窗口并通过ShowWindow 函数来显示。
10.2 WINDOWS API 10.2 WINDOWS API 消息循环:消息循环通常为一个whi1e循环,当为“退出程序”消息时,结束循环,否则就调用相关函数进行消息的转换与分发。 WndProc函数:此函数主要用于程序中各消息的处理,是程序员主要编写的函数,WndProc函数通常为一个switch结构,以消息作为多路分支的条件,在switch的case语句中对需要捕获的消息编写处理代码,对于本程序无需捕获与处理的消息则可以通过DewindowProc 函数转交操作系统处理。本例中就只是处理了显示相关的WM PAINT消息和WM DESTROY消息,其余都转交系统处理。 通过这个程序,应该充分地体会Windows程序的消息运行机制,可以说Windows程序设计是以用户为中心的程序设计、是“被动”式的程序设计。
10.3 MFC编程 在C++中,事物之间的关系是通过类的派生和类的成员函数的重载来反映的。而在Visual C++ 6.0中,类同样是最重要的元素。如一个字符串可以是类Cstring的对象,一个窗口可以是类CWnd的对象,一个对话框可以是类Cdialog的对象等等。 Visual C++ 6.0中的MFC AppWizard(应用程序向导)可以使用户自动生成一些常用的标准程序结构和编程风格。例如一般的Windows应用程序结构、DLL应用程序结构、单文档(SDI)应用程序结构、多文档(MDI)应用程序结构等。
10.3 MFC编程 • Microsoft提供了一个基础类库MFC(Microsoft Foundation Class),其中包含用来开发C++应用程序和Windows应用程序的一组类。 下面详细介绍利用MFC AppWizard创建一个Windows应用程序的步骤:
第零步 启动Visual C++ 6.0,选择【File】菜单下的【New】菜单项,选择【Projects】标签,选择【MFC AppWizard(exe)】项目类型,在【Project name】中输入项目名MyExp,定位于“E:\C++\EXAMPLE\”文件夹中,单击【OK】按钮。
第一步 选择【Single document】应用类型,即单文档应用程序,其他使用默认值,单击【Next】按钮。 第二步 让用户选择程序中是否加入数据库支持,在此使用默认值【None】,单击【Next】按钮。 第三步 让用户选择在程序中加入复合文档,自动化或ActiveX控件的支持,在此使用默认值,单击【Next】按钮。 第四步 让用户选择应用程序的一些特性,在此使用默认值,单击【Next】按钮。 第五步 让用户选择应用程序主窗口的风格、在源文件中是否加入注释、使用怎样的MFC类库,在此使用默认值,单击【Next】按钮。
第六步 用户可以对MFC AppWizard提供的缺省类名、基类名、头文件名、源文件名进行修改,使用默认值,单击【Finish】按钮。则会显示出用户在前面几个步骤中作出的选择内容,单击【OK】按钮,系统开始创建应用程序,并回到Visual C++ 6.0的主界面。 运行过的应用程序MyExp可以脱离Visual C++ 6.0单独运行,运行该文档可以双击“E:\C++\EXAMPLE\Debug”下的MyExp.exe文件。和所有的Windows应用程序一样,MyExp也包含标题栏、菜单栏、工具栏、状态栏等窗口元素。
类说明 AppWiZard在生成应用程序时,共派生了5个类单击“MyExp classes”左侧的“+”展开所有的类,即可显示出应用程序MyExp的5个类,其中:
CAboutDlg: 关于About对话框的对话框类 CMainFrame: 主框架窗口类 CMyExpApp: 应用程序类 CMyExpDoc: 文档类 CMyExpView:视图类 在工程中,每个类都拥有自己的类定义文件(*.H)和类实现文件(*.CPP)。类定义文件主要保存各种类的定义;类实现文件主要保存各种类的成员函数的实现代码。
5个类说明 • 关于About对话框的对话框类CAboutDlg • CAboutDlg是工程MyExp的对话框类,它是由MFC类库中的CDialog类派生而来的。 • 主框架窗口类CMainFrame • CMainFrame是工程MyExp的主框架窗口类,它的基类是CFrameWnd。头文件为MainFrm.h,实现文件为MainFrm.cpp。 • 主框架窗口类用于管理主程序的窗口,来显示窗口的标题栏、工具栏、状态栏等;同时还处理对窗口操作的消息,例如窗口最大化、最小化、改变窗口大小等一般操作。视图窗口是主框架窗口的子集,对于多文档界面(MDI)应用程序,主框架窗口是所有MDI子窗口的容器。
应用程序类CMyExpApp CMyExpApp是工程MyExp的应用程序类,它的基类是CWinApp。头文件为MyExp .h,实现文件为MyExp.cpp。 应用程序类管理程序的整体,控制应用程序的所有对象,包括文档、视图和边框窗口,并完成应用程序的初始化工作和程序退出时的清除工作。 注意:每个基于MFC的应用程序都必须有一个从CWinApp类派生的对象。 文档类CMyExpDoc CMyExpDoc是工程MyExp的文档类,它的基类是CDocument。头文件为MyExpDoc.h,实现文件为MyExpDoc.cpp。
文档类负责存放程序的数据并读取磁盘文件数据,或将磁盘文件数据写入磁盘文件中。文档类必须通过视图类实现与用户的交互。文档类负责存放程序的数据并读取磁盘文件数据,或将磁盘文件数据写入磁盘文件中。文档类必须通过视图类实现与用户的交互。 • 视图类CMyExpView • CMyExpView是工程MyExp的视图类,它的基类是CView。头文件为MyExpView.h,实现文件为MyExpView.cpp。 • 视图类主要负责管理视图窗口,显示文档类中的数据,可以显示在屏幕上,也可以输出到打印机或其他设备上;也负责处理用户数据,接受用户对数据的鼠标、键盘操作并传送给文档类对象。
文件说明 App Wizard在自动生成工程时,除了生成上面介绍的各个类的头文件和实现文件外,还生成了一些为建立应用程序所需要的其他文件。这些文件可以在应用程序生成时指定的路径(E:\C++\EXAMPLE\MyExp)中找到,下面分别对这些文件加以说明:
1. MyExp.clw:Class Wizard的信息存储文件,存储由Class Wizard编辑已有的类或添加新类的信息,存储由Class Wizard建立和编辑各种消息处理函数和映射变量等的信息。 2. MyExp.dsp:Project文件,即工程文件。Project文件中保存着工程的各种信息,一个工程文件对应一个工程应用程序。 3.MyExp.dsw:Workspace文件。一个Workspace文件可包含一个或多个工程。
4.MyExp.rc:资源定义文件,包含程序资源的定义,其中保存了应用程序中使用到的各种资源的信息,包括存贮在文件夹中的图标、位图和光标等。4.MyExp.rc:资源定义文件,包含程序资源的定义,其中保存了应用程序中使用到的各种资源的信息,包括存贮在文件夹中的图标、位图和光标等。 5.Resource.h:标准的头文件,它包含了所有资源符号的定义,与MyExp.rc文件相对应。 6.StdAfx.h:包含在所有App Wizard程序中的标准头文件,它用于包含其他包含在预编译头文件中文件。 7.StdAfx.cpp:包含在所有App Wizard程序中,它包含所有标准include文件。 8.res\MyExpDoc.ico:文件中包含了打开文档时所用的图标文件。
9.res\MyExp.rc2:资源定义文件,包含了用vc不能直接编辑的资源。可以将所有不能由资源编辑器编辑的资源放置到这个文件中。9.res\MyExp.rc2:资源定义文件,包含了用vc不能直接编辑的资源。可以将所有不能由资源编辑器编辑的资源放置到这个文件中。 10.res\MyExp.ico:应用程序的图标文件。应用程序图标包含在资源文件MyExp.rc中。 11.res\Toolbar.bmp:用于创建工具栏按钮的位图文件。初始栏工具栏和状态栏是在主边框窗口类中构造的。 12.ReadMe.txt:包含了对所有该程序的文件的解释信息,并说明了所有创建的类。 另外,如果在App Wizard的第4步中选择了Context_sensitive Help选项,则App Wizard会自动生成一个.hpj文件和一些.rtf文件,它们用以给出上下文的帮助。
在本课程中,我们以两种方式使用类: • 程序框架,如CView,CDocument类(难度大,数量少) • 局部应用,如CString,CRect,CFile等(难度小,数量较多)
典型的Windows应用程序结构 • 控制台应用程序(1-9章) • 基于框架窗口的应用程序 (二个类) • 基于对话框的应用程序 (14章涉及) • 基于文档/视图结构的应用程序 (本教材的主要程序结构)
10.4 单文档界面(SDI)应用程序 • 四个主要类 • 主函数WinMain()隐藏 • CWinApp派生出CMyApp 类 • 注: • 开始时,完全理解四个类是困难的; • 决定什么时候调什么函数是Windows的事情,初学者应重点学习编制相应的消息响应函数!
10.5 在窗口的客户区输出文字和图形 • SDI版的“Hello,VC++” • 通过Workspace的ClassView迅速定位到OnDraw函数,并加入代码: void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here pDC->Rectangle(10,10,110,110); pDC->TextOut(15,50,"Hello world!"); }
学习编程的提示 • 很难在开始将向导生成的全部程序搞清楚; • 很难在开始将某个类的全部方法(成员函数)掌握,如CDC类的方法 • 在开始时,最重要的是: • 以什么方法建立消息处理函数 • 搞清楚数据的 定义、初始化、修改、使用的位置及至少一种方法(以某个例子为记忆依托) • CDC的最常用方法(将频繁使用)
CDC类的重要成员函数 • 文字信息显示 BOOL TextOut(int x, int y, LPCTSTR lpszString) • 画线 CPoint MoveTo ( int x, int y ); BOOL LineTo ( int x, int y ); • 绘制矩形 BOOL Rectangle ( int x1, int y1, int x2, int y2 ); • 获取客户区的坐标 void GetClientRect( LPRECT lpRect );
如何获取CDC的环境(指针) • 在OnDraw中 void CMyView::OnDraw(CDC* pDC) { …… pDC->Rectangle(10,10,110,110); pDC->TextOut(15,50,"Hello world!");} • 在其它函数中 CClientDC dc(this); dc.Rectangle(10,10,110,110); dc.TextOut(15,50,"Hello world!");
增强的OnDraw函数(例10-1 改进 ) CRect rectClient, rectTitle; int nWidth = 100; int nHeight = 40; GetClientRect(&rectClient); //得到当前客户区的尺寸 rectTitle.left = (rectClient.Width()-nWidth)/2; rectTitle.top = (rectClient.Height()-nHeight)/2; rectTitle.right = rectTitle.left+nWidth; rectTitle.bottom = rectTitle.top+nHeight; //根据客户区尺寸计算矩形位置 pDC->Rectangle(rectTitle); pDC->TextOut(rectTitle.left+10, rectTitle.top+10, "Hello, MFC!");
例10-2 简单折线图 //轴的原点,轴的最大值 const int xOrg = 50; const int yOrg = 350; const int xMax = 700; const int yMin = 20; //画轴 pDC->MoveTo(xOrg,yOrg); pDC->LineTo(xMax,yOrg); pDC->MoveTo(xOrg,yOrg); pDC->LineTo(xOrg,yMin);
例10-2 简单折线图(续) //画折线 pDC->MoveTo(101,320); pDC->LineTo(168,280); pDC->LineTo(182,100); pDC->LineTo(200,200); pDC->LineTo(320,330); pDC->LineTo(500,210); pDC->LineTo(520,180); pDC->LineTo(550,143); pDC->LineTo(586,88); pDC->LineTo(612,45);
例10-2 简单折线图(续) //写轴标题 int x = (xMax-xOrg)/2; int y = yOrg + 10; pDC->TextOut(x, y, "Force"); x = xOrg - 20; y = (yOrg - yMin)/2; pDC->TextOut(x, y, "D"); y += 15; pDC->TextOut(x, y, "i"); y += 15; pDC->TextOut(x ,y, "p");
10.6编制消息处理函数 • 消息映射 概念 • 消息映射是使用Visual C++来进行windows应用程序设计中的一部分; • 不用编写WinMain()函数发送消息到WinProc()函数,然后再编写一个WinProc()函数来检测消息类型,最后调用其他函数 • 通过消息映射,消息和它的处理函数才能对应起来。 • ClassWizard来创建新类、定义消息处理函数、覆盖MFC的虚函数 • 利用ClassWizard编制消息处理函数
10.6编制消息处理函数 • Windows 应用程序一般是由消息驱动的(message-driven),这也是Windows编程方式与其他编程方式最大的不同之处。 • 消息就是操作系统通知应用程序某件事情已经发生的一种方式。例如,当用户移动或双击鼠标、改变窗口大小等,都将向适当的窗口发送消息,一个窗口可以向另一个窗口发送消息,并且大多数窗口通过向其他窗口发送一个稍有不同的消息来响应消息。 • Windows系统中的消息主要有三种类型:标准的Windows消息、控件消息和命令消息。
10.6编制消息处理函数 • 标准的Windows消息 • 所有以WM_为前缀的消息都是标准的Windows消息(WM_COMMAND消息除外),如WM_PAINT、WM_QUIT等,这些消息通常含有用于确定如何对消息进行处理的一些参数。标准的Windows 消息一般由窗口对象和视图对象进行处理。窗口对象指的是从CWnd中派生出的类的对象,如从CWnd、CFrameWnd、CMDIFrameWnd、CMDIChildWnd、Cview、Cdialog等MFC类或这些类的派生类定义的对象(这些类都是CWnd类直接或间接派生的)。
10.6编制消息处理函数 • 标准的Windows消息可以分为三类,即键盘消息、鼠标消息和窗口消息。下面分别加以说明。 • 键盘消息 • 当键盘的某个键被按下时,将产生WM_CHAR消息,这个消息带有字符代码值、重复次数和先前状态码三个参数。WM_CHAR消息的处理函数为OnChar( ),此函数的原型为: • afx_msg void Onchar(UINT nChar,UINT nRepCnt,UINT nFlags); • 其中:nChar是所按键的字符代码值; • nRepCnt是重复的次数,它表示用户按键时重复击键的次数; • nFlags表示扫描码、先前键状态和键转换状态等,其参数值功能描述见表4.1所示。
位 含 义 0~15 指明键重复按下的次数 16~23 指定扫描码,依赖于具体厂家的按键值 24 如果同时按下扩展键(如功能键Alt、Ctrl),则置为1,否则置为0 25~28 由Windows内部使用 29 若同时按下了Alt键,则置为1,否则置为0 30 指明先前键状态。若消息发出前键是按下的,则置为1,否则置为0 31 指明键转换状态。若按键已松开,则置为1,否则置为0 nFlags参数值功能描述
鼠标消息 在Windows中处理鼠标的操作基本上有三种: 单击(Click) 2. 双击(Double Click) 3.拖动(Drag) 所有这些鼠标的操作,都会产生相应的消息。鼠标移动时产生WM_MOUSEMOVE消息,鼠标左键按下时产生WM_LBUTTONDOWN消息,鼠标右键按下时产生WM_RBUTTONDOWN消息,鼠标左键双击时产生WM_LBOTTONDBLCLK消息等等。 所有这些鼠标消息的处理函数都有相似的原型,它们都有两个参数。我们以处理鼠标左键按下的消息函数OnLButtonDown( )为例来进行说明。此函数的原型如下: afx_msg void OnLButtonDown(UINT nFlags,Cpoint point); 其中:参数point是鼠标事件发生时鼠标光标所在的位置,它是相对于窗口左上角的水平坐标和垂直坐标而言的。 10.6编制消息处理函数
位屏蔽 含 义 MK_CONTROL Ctrl键按下时设置 MK_LBUTTON 鼠标左键按下时设置 MK_MBUTTON 鼠标中间键按下时设置 MK_RBUTTON 鼠标右键按下时设置 MK_SHIFT Shift键按下时设置 10.6编制消息处理函数 参数nFlag指明鼠标按钮的状态以及鼠标事件发生时键盘上某些键的状态,每一状态都可以由nFlag的一位来表示。我们可以从表4.2中所列的位屏蔽中得到每一位。nFlags参数每位的含义 其中,MK_CONTROL、MK_LBUTTON、MK_MBUTTON、MK_MBUTTON、MK_RBUTTON和MK_SHIFT都是预定义的宏。
窗口消息 所有窗口的变化,包括窗口内容重绘WM_PAINT、窗口最大化WM_MAXIMIZE、窗口最小化WM_MINIMIZE、窗口重定义大小WM_RESIZE、窗口滚动WM_HSCROLL、WM_VSCROLL、窗口定时WM_TIMER等消息所带参数各不相同。 在这里我们来详细讨论一下WM_PAINT消息。当调用成员函数UpdateWindow( )或 RedrawWindow( )要求重新绘制窗口内容时,应用程序将收到WM_PAINT消息。当窗口最小化后再还原或被其他窗口遮盖后又重新显示时,则当前窗口中的内容必须重新绘制,消息WM_PAINT就是为实现这个功能的。 10.6编制消息处理函数
当向Windows应用程序发送WM_PAINT消息,应用程序检索到此消息后,就重新显示窗口中的内容。WM_PAINT消息的处理函数为 OnPaint( )。 函数原型为: afx_msg void OnPaint( ); 如果想详细了解所有的标准的Windows消息,可以参阅Visual C++ 6.0联机帮助中的有关内容。 4.4.2 控件消息 控件是一个小的子窗口,属于其他窗口(如对话框等),能够接受操作并象父窗口发送消息。常见的控件有按钮、列表框、编辑框、复合框、滚动条等。 在Visual C++ 6.0中,对控件的操作都是通过生成相应的控件类来进行。这些控件类仅能发送少量特定的消息,这些消息就叫做控件消息。发送控件消息的控件在Visual C++中 10.6编制消息处理函数
使用唯一ID号来进行标识,使用控件类来操纵。使用唯一ID号来进行标识,使用控件类来操纵。 控件消息分为两类: 1. 从控件传给消息,通常这类消息的前缀的最后一个字符为N; 2. 由系统发送给控件的消息,这类消息的前缀的最后一个字符为M。 例如,当用户对编辑框中的文本进行修改时,编辑框将发送给父窗口一条包含控件通知码EN_CHANGE的WM_COMMAND消息。窗口的消息处理函数将以某种适当的方式对通知消息作出响应,如检索编辑框中的文本。 与其他标准的Windows消息一样,控件消息也是由窗口对象和视图对象进行处理。 10.6编制消息处理函数
消息映射 概念 • 消息绑定机制 • 消息映射宏 • 函数原型 • 函数实现 • 消息在SDI各类中的传递过程如下: • 视图类文档类框架类应用程序类 • 消息处理的时机 • 可以在任何类中 • 本教材主要在视图类中处理