Windows消息与命令_第1页
Windows消息与命令_第2页
Windows消息与命令_第3页
Windows消息与命令_第4页
Windows消息与命令_第5页
已阅读5页,还剩181页未读 继续免费阅读

下载本文档

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

文档简介

1、10.1 Windows消息与命令消息与命令10.2 单文档与多文档程序单文档与多文档程序10.3 对话框与常用组件对话框与常用组件10.4 通用对话框与通用对话框与Windows 95控件控件第第 10 章章 Windows用户界面用户界面返回主目录10.1 Windows消息与命令消息与命令 10.1.1 消息驱动机制消息驱动机制 消息是一种报告有关事件发生的通知,类似于DOS下的用户输入。Windows应用程序是由消息驱动的。Windows操作系统允许多个任务同时运行,应用程序的输入输出由Windows统一管理;Windows系统下每一个窗口都维护一个消息队列,操作系统接收和管理所有输入

2、消息、系统消息,并把它们发送给相应窗口的消息队列。应用程序初始化完成后,进入消息循环,维护自己的消息队列,从中取出消息,对其进行处理。编写消息处理函数是Windows编程的主要工作之一。 Windows应用程序的消息来源有以下四种: (1) 输入消息:包括键盘和鼠标的输入。这一类消息首先放在系统消息队列中,然后由Windows将它们送入应用程序消息队列中,由应用程序来处理消息。 (2) 控制消息:用来与Windows的控制对象,如列表框、按钮、检查框等进行双向通信。当用户在列表框中改动当前选择或改变了检查框的状态时发出此类消息。这类消息一般不经过应用程序消息队列,而是直接发送到控制对象上去。

3、(3) 系统消息:对程序化的事件或系统时钟中断作出反应。一些系统消息,像DDE消息(动态数据交换消息)要通过Windows的系统消息队列,而有的则不通过系统消息队列而直接送入应用程序的消息队列,如创建窗口消息。 (4) 用户消息:这是程序员自己定义并在应用程序中主动发出的,一般由应用程序的某一部分内部处理。 基于MFC类库的应用程序完成初始化后,调用Run( )函数进入消息循环。在CWnd类(所有窗口类的基类)中预定义标准Windows消息的处理函数,处理函数的名称以“On”开始,用户可以根据需要重写这些函数。 在CWnd类中,标准Windows消息的处理函数声明都带有afx_msg关键字,例

4、如消息 WM_PAINT的处理函数被声明为: afx_msg void OnPaint( ); 关键字afx_msg用于把消息处理函数与其它CWnd成员函数分开,这些函数是通过消息映射实现的,依赖于标准的预处理宏。预处理之后,关键字afx_msg的位置就变成了空白。以全局对象theApp为例,其中的消息映射如下: BEGIN_MESSAGE_MAP(CEx01App, CWinApp) /AFX_MSG_MAP(CEx01App)ON_COMMAND(ID_APP_ABOUT, OnAppAbout) / 注意:ClassWizard(类向导)将可能添加或删除消息映射宏/AFX_MSG_MAP

5、/ 标准的基于文档的文件命令O N _ C O M M A N D ( I D _ F I L E _ N E W , CWinApp:OnFileNew)O N _ C O M M A N D ( I D _ F I L E _ O P E N , CWinApp:OnFileOpen)/ 标准的打印设置命令ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp:OnFilePrintSetup)END_MESSAGE_MAP( ) 其 中 的 B E G I N _ M E S S A G E _ M A P 和END_MESSAGE_MAP都是预处理宏,用于声明消

6、息映射的开始和结束。而在类中重新修改的消息处理函数声明形式为:/Generated message map functionsprotected:/AFX_MSG(CEx01Ciew)afx_msg void OnPaint( );/AFX_MSGDECLARE_MESSAGE_MAP( ) 最后的DECLARE_MESSAGE_MAP宏,声明在该类中使用消息映射。 10.1.2 应用程序菜单应用程序菜单 Windows菜单是应用程序命令项列表,菜单项可以是文字或位图。通过选择菜单项使应用程序完成与菜单项相关的命令。 下面通过一个例子来介绍如何在应用程序中建立新菜单、如何在菜单中加入新的菜单项

7、、如何为菜单项建立相应的命令处理函数以实现消息映射。 首先看看如何在已有的下拉菜单中增加一个菜单项。 第一步,编辑菜单项资源,步骤如下: (1) 运行AppWizard创建Ex02(参看9.5.2 节的“利用AppWizard建立一个新项目”),选中“Single document”(单文档SDI)单选项后,直接按下Finish按钮。 (2) 在项目工作区窗口中打开ResourceView(资源视图)。 (3) 双击该视图中的Menu图标,展开菜单资源。 (4) 双击IDR_MAINFRAME,打开菜单编辑器,如图10.1所示。 (5) 可以看到,在“文件”菜单的最下方有一个空白菜单项,用户可

8、以编辑这个菜单项来添加菜单项。也可以单击某个菜单项,然后按键盘上的Insert键,在选定的菜单项前面插入一个新的菜单项。 (6) 双击空白菜单项,打开Menu Item Properties对话框,如图10.2所示。图10.1 菜单编辑器图10.2 Menu Item Properties对话框 (7) 在Caption文本框中输入新菜单项名,输入的名字是“Demo”。在ID组合框中输入新菜单项的ID。注意,这个ID必须是唯一的,不能和别的ID重名。一般菜单项ID的命令规则是:ID_菜单名_菜单项名。因此将该菜单项命名为ID_FILE_DEMO。 (8) 关闭该对话框,新的菜单项已经添加到原有

9、的菜单中。运行这个程序可以看到,在“文件”菜单的最下方有一个“Demo”菜单项,如图10.3所示。 注意:这里选SDI类型,一是为了简化程序设计,二是想让读者对比SDI应用程序(如本例的Ex02)和MDI应用程序(如第9章的Ex01)在外观和使用上的一些区别,对SDI和MDI有一个简单了解。关于SDI和MDI应用程序将在后续章节中向读者详细介绍。 第二步,进行消息映射。图10.3中Demo菜单项是灰色的,不可用,因为它没有命令处理函数。为此必须要对菜单项进行消息映射,为新的菜单项建立处理函数。操作步骤如下: (1)在View菜单中选择Class Wizard选项或使用加速键Ctrl+W,打开C

10、lassWizard对话框,选中其中的Message Maps选项卡,如图10.4所示。 (2)在Class Name下拉列表框中选择CEx02View。 (3)在Object IDs列表框中选择菜单项Demo的ID,即ID_FILE_DEMO。 (4)在Messages列表框中单击COMMAND,此时Add Function按钮变亮,表示可以 图10.3 添加了Demo菜单项的应用程序图10.4 ClassWizard 的Message Maps选项卡 添加处理函数。单击Add Function按钮或双击COMMAND,弹出Add Member Function对话框,如图10.5所示。 在

11、Member function name编辑框中给出了系统推荐使用的函数名是OnFileDemo。通常可以使用这个缺省的名字,如果有特殊需要也可以修改它。单击OK按钮,这时可以在Member functions列表框中看到Demo菜单项的处理函数已经创建,如图10.6所示。也就是说,当用户选择Demo菜单项时,应用程序将调用OnFileDemo函数实现消息映射。 在图10.6中单击OK按钮完成消息映射。 图10.5 Add Member Function对话框图10.6 创建Demo的处理函数OnFileDemo 第三步,编写响应程序代码。现在为OnFileDemo函数编写实际的处理程序。比如

12、,当用户选择菜单项Demo时,在窗口中显示“您选择了Demo菜单项。”信息。操作步骤如下: (1) 在文档类CEx02Doc中声明一个CString对象m_String。从上一章可以知道,程序中的文档对象用于存储在视图中的数据,因此这里选择在文档类中创建字符串对象。 在项目工作区工作区中选ClassView类视图,双击CEx02Doc,打开类编辑器,如图10.7所示。对CEx02Doc.h头文件中修改CEx02Doc类的定义: class CEx02Doc : public CDocument protected: / create from serialization onlyCEx02Do

13、c( );DECLARE_DYNCREATE(CEx02Doc)CString m_String;/输入本行内容 程序中的黑体字部分表示需要输入的内容,本书下面章节的片段中均使用此方法。“m_”前缀声明成员变量是Visual C+的标准,读者应该习惯于使用这种变量声明方法。 (2) 在CEx02Doc的构造函数中初始化m_String。在类视图中单击文档类CEx02Doc前面的“+”,将其展开。双击CEx02Doc( )函数,将会在正文窗口内打开代码编辑器。对CEx02Doc( )函数编辑如下:CEx02Doc:CEx02Doc( ) / TODO: add one-time construc

14、tion code here m_String=; /输入本行内容 (3) 编辑OnFileDemo( )函数,将m_String赋值为“您选择了Demo菜单项。”。在图10.6(若在屏幕上看不到它,按下Ctrl+W加速键)的Member function列表框中单击OnFileDemo,然后按下Edit Code按钮或双击OnFileDemo, 出现Ex02View.cpp代 码 编 辑 窗 口 , 并 且 光 标 已 经 处 于 需 要 编 辑 的 函 数OnFileDemo中。编辑OnFileDemo( )函数: void CEx02View:OnFileDemo( ) / TODO:

15、Add your command handler code hereCEx02Doc* pDoc=GetDocument( ); /输入以下四行内容ASSERT_VALID(pDoc);pDoc-m_String=您选择了Demo菜单项。;Invalidate( ); 由于对象m_String处于文档类CEx02Doc中,要在视图类中对这一对象进行存取,必须先获得一个指向文档对象的指针,这可通过GetDocument函数完成。ASSERT_VALID( )函数则用于确保一定能获取该指针,这是Visual C+的技术,如果不使用这一函数,可能会产生错误信息。Invalidate( )函数使客户区

16、内容失效,强迫程序调用OnDraw( )函数,重新绘制视图。 (4) 用OnDraw()函数重新绘制客户区。视图对象管理程序客户区,对客户区的绘制工作由成员函数OnDraw( )完成。几乎所有应用程序的绘制工作都通过这一函数实现,编程时必须修改这个函数。 在项目工作区中单击类视图中CEx02View前面的“+”将其展开,双击OnDraw( )函数,打开CEx02View.cpp编辑窗口,光标位于OnDraw( )函数中。按下述内容对OnDraw函数进行编辑:void CEx02View:OnDraw(CDC* pDC)CEx02Doc* pDoc = GetDocument( );ASSERT

17、_VALID(pDoc);/ TODO: add draw code for native data herepDC-TextOut(0, 0, pDoc-m_String); /输入本行 这里使用了CDC类的成员函数TextOut( )函数,该函数用于在CDC设备类中显示字符串。它有三个参数,前两个参数用于指示字符串显示的相对位置,第三个参数用于传递要显示的字符串。 现在编译运行这个程序。在“文件”菜单中选择Demo菜单项,窗口中显示“您选择了Demo菜单项。”的信息,如图10.8所示。 建立新菜单的方法与添加菜单项类似,首先编辑菜单资源,然后为相应的菜单项建立消息映射,编写消息处理函数。在

18、资源编辑器中建立新菜单的一般步骤如下:图10.8 Ex02程序运行的结果 (1) 打开菜单编辑器,可以看到在菜单的最右边有一个空白的菜单项,如图10.9所示。 (2) 编辑该空白菜单项(双击它,出现对话框),将其命名为NewMenu,如图10.10所示。可以看到,Pop-up复选框是被选中的,表示本菜单带有弹出式子菜单,因此无须定义本菜单的ID。 (3) 关闭Menu Item Properties对话框,NewMenu菜单下方已经自动增加了一个空白的菜单项,如图10.11所示。用户可以通过编辑该空白菜单项,向NewMenu菜单添加菜单项。图10.9 菜单编辑器 图10.10 New Item

19、 Properties对话框图10.11 为NewMenu添加菜单项 (4) 向NewMenu菜单中添加两个菜单项New1和New2,并 将 其 I D 分 别 命 名 为 I D _ N E W M E N U _ N E W 1 和ID_NEWMENU_NEW2,如图10.12所示。 在Windows中常常可以看到,点击某个菜单项还可以显示下一级子菜单。例如为NewMenu的New2添加子菜单Sub1和Sub2的步骤如下: (1) 在菜单编辑器中双击New2菜单项,打开Menu ItemProperties对话框,如图10.13所示。 (2) 选中对话框中的Pop-up复选框,此时ID下拉

20、列表框中的ID_NEWMENU_ NEW2自动消失。关闭该对话框后,在菜单项New2右边出现一个黑色的三角和一个空的子菜单,如图10.14所示。 图10.12 新菜单建立完成图10.13 Menu Item Properties对话框图10.14 New2带有下一级子菜单 (3) 编辑New2的空白子菜单,添加Sub1和Sub2两个菜单项 , 如 图 1 0 . 1 5 所 示 。 并 将 它 们 的 I D 分 别 命 名 为ID_NEWMENU_NEW2_SUB1和ID_NEWMENU_ NEW2_SUB2。 无论菜单项在哪一级菜单中,建立它们的命令处理函数的方法都是一样的。因为所有的菜单

21、项都有一个唯一的ID标识,菜单项所处的位置不影响它们的处理函数的编写。 图10.15 为NewMenu添加子菜单 10.1.3 快捷键和加速键快捷键和加速键 1. 快捷键快捷键 “文件”菜单中的“F”带有下划线,这表示用户可以通过按下Alt+F打开“文件”菜单,而无须用鼠标选取。F称为“文件”菜单的快捷键。 增加快捷键非常简单,只要在给定菜单名或菜单项名中的某个位置多写一个“&”字符,就把该字符后面的那个字符定义成了快捷键。比如把NewMenu中的字母Mp定义为快捷键,只需将菜单名设置为New&Menu即可,如图10.16所示。 图10.16 增加快捷键 2加速键加速键 加速键

22、是用于某个菜单项的控制键,用户可以通过按这些键直接打开相应的菜单选项,而无须打开菜单选取。例如,在Windows中,按Ctrl+V与“编辑”菜单中的“粘贴”选择是相同的。 注意,只有菜单项才有加速键。 下面是向NewMenu菜单中的New1菜单项增加加速键Ctrl+F1的步骤: (1) 在项目工作区的资源视图中双击Accelerator图标。 (2) 双击IDR_MAINFRAME图标,打开加速键编辑器,如图10.17所示。 (3) 双击加速键编辑器中的最后一个空白项,打开Accel Properties对话框,如图10.18所示。图10.17 加速键编辑器图10.18 Accel Prope

23、rties对话框 (4) 在ID下拉列表框中选择New1菜单项的ID,即“ID_NEWMENU_NEW1”。 (5) 在Key下拉列表框中选择VK_F1,关闭对话框,把Ctrl+F1加入到New1菜单项中。 (6) 最后还要将New1的标题改为“New1 Ctrl+F1”。 10.1.4 工具栏和状态栏工具栏和状态栏 1工具栏工具栏 工具栏显示一组按钮,每个按钮对应一个菜单项。工具栏按钮是方便直观的用户界面,用户可以直接点击某个按钮,其作用和选取相应菜单选项是相同的。 可以把任何菜单项连接到工具栏中的按钮。以“文件”菜单中的Demo菜单项为例,将其添加到工具栏的步骤如下: (1) 在项目工作区

24、的资源视图中,双击Toolbar图标。 (2) 双击IDR_MAINFRAME图标,打开工具栏编辑器,同时在它的右边出现一组工具栏绘图工具,如图10.19所示。图10.19 工具栏编辑器 (3) 选取工具栏编辑器中工具栏最右边的空白按钮,使用绘图工具在新按钮中画出如图10.19所示的图形。 (4) 双击工具栏编辑器中编辑好的新按钮,打开Toolbar Button Properties对话框,如图10.20所示。 (5) 在ID下拉列表框中选择ID_FILE_DEMO,新的按钮已经连接到Demo菜单项上。运行程序,单击新按钮,可以看到窗口中显示出与选择Demo菜单项相同的信息,如图10.21所

25、示。 2状态栏状态栏 状态栏用于显示某些提示信息。当用户把鼠标移到某个菜单项或工具栏按钮上时,状态栏中通常会显示一些简单的信息,提示用户该选项可以做哪些操作。图10.20 Toolbar Properties对话框图10.21 添加了工具栏按钮的应用程序 增加工具栏按钮的状态栏提示,只需在图10.20所示的Toolbar Properties对话框的Prompt文本框中放置相应信息即可。其操作步骤如下: (1) 打开需要增加提示的按钮的Toolbar Properties对话框。这里为Demo按钮增加提示信息,如图10.22所示。 (2) 在Prompt文本框中输入这个按钮的说明:“在窗口显示

26、相关的信息n演示信息”,这意味着当用户将鼠标移到Demo按钮上时,状态栏中将显示“在窗口显示相关信息”,而在鼠标旁边将显示“演示信息”。 再次运行程序,看一看当鼠标移到Demo按钮上时,是否会出现如图10.23所示的提示信息。图10.22 为工具栏按钮增加提示信息图10.23 添加了提示信息的工具栏按钮10.2 单文档与多文档程序单文档与多文档程序 10.2.1 文档文档视图结构视图结构 使用MFC AppWizard创建应用程序,首先要求用户确定应用程序的基本结构:单文档(SDI)、多文档(MDI)或基于对话框的应用程序界面。其中单文档或多文档创建的应用程序是基于文档的应用程序。 创建基于文

27、档的应用程序一般需要经过以下几步: 为每一种类型的文档定义一个派生自CDocument类的派生类; 添加用于存储文档数据的成员变量; 编写用于实现读取和修改文档数据的函数成员。 在MFC中,用文档视图结构可将数据从用户对数据的观察中分离出来。文档用来管理应用程序的数据,视图用于显示文档并管理与用户的交互过程。实际上,视图在用户与文档之间起桥梁作用,如图10.24所示。 MFC AppWizard创建的文档是从CDocument类派生的,视图类是从CView类派生的。 CDocument类为应用程序定义的文档类提供基本功能,而CView类为应用程序定义的视图类提供基本功能。视图和文档连接在一起,

28、在文档与用户间起中介作用。视图在屏幕上显示文档数据并把用户输入转换成对文档的操作。 图10. 24 文档视图之间的关系 10.2.2 单文档应用程序的建立单文档应用程序的建立 上一节的例子通过菜单显示了一行字符,是通过菜单完成的,本节直接在应用程序的窗口中显示一些信息,如“Visual C+的单文档应用程序很简单”,按如下步骤进行操作: (1) 利用MFC AppWizard创建单文档的应用程序Ex03。 (2) 在项目工作区的类视图中单击CEx03View类前面“+”,展开该类。 (3) 双击OnDraw( )函数名,打开Ex03View.cpp编辑窗口。 (4) 对OnDraw( )函数按

29、如下内容编辑: void CEx03View:OnDraw(CDC* pDC)CEx03Doc* pDoc = GetDocument( );ASSERT_VALID(pDoc);CString m_Message= Visual C+的单文档应用程序很简单;pDC-TextOut(30, 30, m_Message); 现在编译运行程序。“Visual C+的单文档应用程序很简单”显示在程序窗口中,但是,一个好的程序必然少不了与用户之间的交互。在Windows中,用户最常用的输入工具无非是键盘和鼠标,如果要显示由键盘输入的字符串,应该如何处理呢? 下面演示如何处理键盘和鼠标输入以及存储和读取

30、文档数据。 首先,建立键盘输入存储区,用于存储键盘输入的字符串。程序中的文档对象用于存储在视图中显示的数据,因此下面在文档类中创建键盘输入存储区。当用户击键时,把键入的数据加到一个字符串中,并在视图中显示这个字符串。下面修改CEx03Doc类,增加字符串数据成员m_Message: (1) 在类视图中双击CEx03Doc图标, 在Ex03Doc.h编辑窗口修改CEx03Doc的类声明:class CEx03Doc : public CDocumentprotected: / create from serialization onlyCEx03Doc( );DECLARE_DYNCREATE(

31、CEx03Doc)CString m_Message;/在此加入变量声明在此加入变量声明 (2) 在文档类CEx03Doc的构造函数中,把这个字符串变量初始化为空字符串。在类视图中单击CEx03Doc图标前的“+”,展开该类。双击CEx03Doc( )函数,在Ex03Doc.cpp编辑窗口修改CEx03Doc的构造函数:CEx03Doc:CEx03Doc( )/ TODO: add one-time construction code herem_Message= ;/对对m_Message变量初始化变量初始化 现在来处理键盘输入。键盘输入产生WM_CHAR消息,处理键盘输入就必须编写相应的消

32、息处理函数。首先按照下面的步骤使用Class Wizard来完成消息的映射: (1) 按下Ctrl+W打开MFC ClassWizard对话框,并选择Message Maps选项卡。由于需要添加的消息处理函数属于CEx03View类,在Class name下拉列表框中选择CEx03View,如图10.25所示。 (2) 在Messages列表框中选择WM_CHAR消息,并双击它(或单击App Function按钮),这时可以在Member functions列表框中看到添加了这一OnChar函数。 图10.25 MFC ClassWizard对话框的Message Maps选项卡 现在可以通过

33、OnChar函数来读取键盘输入并将读取的字符存入到m_Message变量中。在图10.25中按下Edit Code按钮,编辑OnChar函数如下:void CEx03View:OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)CEx03Doc* pDoc=GetDocument( ); /获取文档对象指针ASSERT_VALID(pDoc); /确保一定能获取该指针pDoc-m_Message += nChar; /将读取的字符加到m_Message中Invalidate( ); /强行调用OnDraw函数,重绘视图CView:OnChar(nChar,

34、 nRepCnt, nFlags); 最后,对OnDraw函数进行修改,去掉对m_Message的声明(已经在CEx03Doc类中声明过),使之成为:void CEx03View:OnDraw(CDC* pDC)CEx03Doc* pDoc = GetDocument( );ASSERT_VALID(pDoc);/ TODO: add draw code for native data herepDC-TextOut(30, 30, pDoc-m_Message); 编译运行该程序并键入某些字符,相应的字符将在窗口中显示出来。 视图中显示的文档数据通过文档的串行化方法来实现磁盘的存取。所谓文档

35、的串行化,是指在文档打开时,能够自动把文档中的数据转换为文档类所支持的对象;在保存文档时,能够自动将文档类对象转换为文档数据格式。 文档串行化方法是由CEx03Doc类中的Serialize函数完成的。在项目工作区的类视图中单击CEx03Doc前面的“+”,展开该类。双击Serialize函数,在代码编辑窗口对其编辑如下:void CEx03Doc:Serialize(CArchive& ar)if (ar.IsStoring( )ar m_Message;/读入文档 现在运行这个程序。在窗口中输入“这是第一次的输入”,再选择“文件”菜单中的“保存”,在“文件名”编辑框中输入“1”。再

36、运行一次,输入“这是第二次的输入”,将其存为“2”,如图10.26所示。图10.26 在Ex03程序中存储文件 现在可以使用“文件”菜单中的“打开”菜单项分别打开这两个文件,可以看到程序窗口中显示的字符串与存储的字符串完全一致。 接下来看看如何处理鼠标输入。按照下面的步骤修改上面的程序,将允许用户通过在客户区的某个位置单击鼠标来确定新文本输入的位置。当用户输入文本时,将会在单击鼠标的位置开始输入;当用户用鼠标选择新位置时,清除字符串对象m_Message中的字符,以便接收新的字符串。 由于鼠标左键被按下时,文本显示位置也随之变化,必须首先建立一个新的存储区,用以记录鼠标单击的位置。鼠标位置可以

37、通过鼠标消息处理函数传递的参数获得。所有鼠标消息处理函数都传递两个参数: UINT nFlags,CPoint point 其中,point参数是CPoint类的一个对象,用于存入鼠标当前位置。可以在程序视图类的头文件中创建一个CPoint类的对象,命名为m_Point,用于记录point参数。编辑Ex03View.h,修改CEx03View类的声明:class CEx03View : public CViewprotected: / create from serialization onlyCEx03View( );DECLARE_DYNCREATE(CEx03View)CPoint m_

38、Point;/用户加入本行用户加入本行 与m_Message对象一样,需要对m_Point做初始化工作,这一工作在视图类的构造函数CEx03View( )中完成:CEx03View:CEx03View( )/ TODO: add construction code herem_Point.x=0; /用户加入以下两行用户加入以下两行/m_Point.y=0; 现在使用ClassWizard创建WM_LBUTTONDOWN消息(被鼠标左键单击)的处理函数,该函数记录鼠标新位置,并在该位置显示新输入的文本。步骤如下: (1) 按下Ctrl+W加速键,启动ClassWizard对话框,选择Messa

39、ge Maps选项卡。 (2) 在Class name下拉列表框中选择CEx03View,在Messages列表框中选择WM_LBUTTONDOWN消息。 (3) 按下Add Function按钮,这时可以在Member functions列表框中看到WM_LBUTTONDOWN的处理函数已被创建,函数名为OnLButtonDown。编辑OnLButtonDown函数如下: void CEx03View:OnLButtonDown(UINT nFlags, CPoint point) / TODO: Add your message handler code here and/or call

40、defaultm_Point=point; /记录鼠标位置CEx03Doc* pDoc=GetDocument( );/定义pDoc使用其指向CEx03Doc对象ASSERT_VALID(pDoc);pDoc-m_Message.Empty();/清空字符串Invalidate(); /强迫重新绘图CView:OnLButtonDown(nFlags, point); 新位置已经记录在m_Point中了。改写OnDraw函数,在新位置显示文本:void CEx03View:OnDraw(CDC* pDC)CEx03Doc* pDoc = GetDocument( );ASSERT_VALID(

41、pDoc);/ TODO: add draw code for native data herepDC-TextOut(m_Point.x, m_Point.y, pDoc-m_Message); 运行程序。在客户区的任何位置单击鼠标左键,然后输入文本,新文本将会在该位置出现。程序中读者可能发现一个问题:输入字符或单击鼠标的时候,无法确认当前字符的输入位置。在Windows中,通常使用光标指示用户输入字符时显示的位置。下面介绍如何在程序中建立和使用光标。 要建立一个新光标,必须确定光标的大小。通常,光标与当前字符高度相同,宽度为平均字符宽度的1/8。文本字符串大小的相关信息由MFC提供的结构类

42、型TEXTMETRIC记录,只要说明该类型的一个变量,然后使用CDC类中的GetTextMetrics方法对其进行填充即可。TEXTMETRIC类型中包括的成员有:tmHeight(字符高度)、tmAveCharWidth(平均字符宽度)、thMaxCharWidth(最大字符宽度)、thWeight(磅数)、tmItalic(斜体)等等。上面程序建立和使用光标的步骤如下: (1) 在视图类中建立指示光标是否已经建立的布尔变量m_Created和光标位置m_Pos:class CEx03View : public CViewprotected: / create from serializat

43、ion onlyCEx03View( );DECLARE_DYNCREATE(CEx03View)CPoint m_Point;BOOL m_Created;/光标是否建立/CPoint m_Pos;/光标位置/ (2) 在视图构造函数中将m_Created初始化为False,表示尚未创建;将m_Pos初始化为0:CEx03View:CEx03View( )/ TODO: add construction code herem_Point.x=0;m_Point.y=0;m_Created=FALSE;m_Pos=m_Point; (3) 使用GetTextMetrics方法获取当前字符的大小

44、,并用CreateSolidCaret方法实际建立光标,编辑OnDraw函数:void CEx03View:OnDraw(CDC* pDC)CEx03Doc* pDoc = GetDocument( );ASSERT_VALID(pDoc);/ TODO: add draw code for native data herepDC-TextOut(m_Point.x, m_Point.y, pDoc-m_Message);if(!m_Created)TEXTMETRIC metric;pDC-GetTextMetrics(&metric);CreateSolidCaret(metric

45、.tmAveCharWidth/8, metric.tmHeight); 创建光标后,使用SetCarePos设置光标位置,并使用ShowCaret在视图中显示光标,还需要将m_Created设置为TRUE, 表示光标已被创建。重新修改OnDraw函数:void CEx03View:OnDraw(CDC* pDC)CEx03Doc* pDoc = GetDocument( );ASSERT_VALID(pDoc);/ TODO: add draw code for native data herepDC-TextOut(m_Point.x, m_Point.y, pDoc-m_Message)

46、;if(!m_Created)TEXTMETRIC metric;pDC-GetTextMetrics(&metric);CreateSolidCaret(metric.tmAveCharWidth/8,metric.tmHeight);SetCaretPos(m_Pos);ShowCaret( );m_Created=TRUE; 现在编译并运行程序。可以看到光标在窗口的左上角闪动。但在新位置点下鼠标左键时,光标并没有跟着移动。 光标需要指示下一个字符的输入位置。因此,当用户输入字符时,光标应该随之移动而出现在字符串的尾部。可按下面的方法处理: (1) 首先计算字符串结尾的位置。通过使

47、用GetTextExtent函数可以获得字符串的高度和长度。需要注意,这里的长度不是指字符串中字符的个数,而是字符串在视图中以像素为单位计算的长度。 (2) 隐藏光标,将光标移动到字符串的尾部并再次显示它。修改OnDraw函数如下:void CEx03View:OnDraw(CDC* pDC)CEx03Doc* pDoc = GetDocument( );ASSERT_VALID(pDoc);/ TODO: add draw code for native data herepDC-TextOut(m_Point.x, m_Point.y, pDoc-m_Message);if(!m_Crea

48、ted)TEXTMETRIC metric;pDC-GetTextMetrics(&metric);CreateSolidCaret(metric.tmAveCharWidth/8,metric.tmHeight);SetCaretPos(m_Pos);ShowCaret( );m_Created=TRUE;CSize StringSize=pDC-GetTextExtent(pDoc-m_Message);/字符串长度m_Pos.x=m_Point.x+StringSize.cx; /计算光标位置m_Pos.y=m_Point.y;HideCaret( ); /隐藏光标SetCare

49、tPos(m_Pos); /移动光标到新位置ShowCaret( ); /显示光标 (3) 最后还应该考虑到,在程序窗口失去输入焦点时,需要隐藏光标,当窗口重新获得焦点时再次显示光标。程序窗口失去焦点和获得焦点的消息分别是WM_KILLFOCUS和WM_SETFOCUS。使用ClassWizard分别创建这两个消息的处理函数,并编辑它们如下: void CEx03View:OnKillFocus(CWnd* pNewWnd) CView:OnKillFocus(pNewWnd);HideCaret( );void CEx03View:OnSetFocus(CWnd* pOldWnd) CVie

50、w:OnSetFocus(pOldWnd);ShowCaret( ); 再次编译运行这个程序,可以看到闪动的光标指示下一个字符的输入位置。当用户用鼠标在其它位置点击时,所输入的字符串消失,光标又在新位置闪动。 10.2.3 多文档应用程序的建立多文档应用程序的建立 单文档应用程序与多文档应用程序在外观上是有区别的。可以再次运行Ex01程序,将它与单文档应用程序相比较(如Ex02、Ex03)。多文档应用程序多出一个“窗口”菜单,并且在主窗口中可以同时打开多个子窗口。这些子窗口中的内容可以是不同的。 利用MFC AppWizard生成单文档(SDI)应用程序时,只派生文档类、视图类、主窗口类和应用

51、程序类。而生成多文档(MDI)应用程序时,它有一个主窗口和嵌在该窗口中的子窗口,所以具有CChildFrame类,而且每个窗口可以使用不同的文档模板。 单文档应用程序的主窗口类派生自CFrameWnd类,而多文档应用程序的主窗口类则是CMDIFrameWnd类的派生类。 在文档模板方面,单文档应用程序的文档模板是CSingleDocTemplate类,多文档应用程序的文档模板则是CMultiDocTemplate类。CSingleDocTemplate类构造函数的用法与CMultiDocTemplate类相同,但从建立文档模板所给定的参数不难发现,单文档应用程序文档模板的框架窗口与其主框架窗口

52、使用相同的类(CMainFrame),而多文档应用程序文档模板的文件框架窗口使用的是子窗口类(CChildFrame)。多文档应用程序中可以有多个子窗口,因此多文档应用程序中可以有多个文档模板。 通过观察单文档和多文档应用程序的InitInstance( )函数,可以明显看出它们之间的差别: /单文档应用程序的InitInstance( )函数BOOL CEx02App:InitInstance( )/建立文档模板CSingleDocTemplate* pDocTemplate;pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIM

53、E_CLASS(CEx02Doc),RUNTIME_CLASS(CMainFrame), / main SDI frame windowRUNTIME_CLASS(CEx02View);AddDocTemplate(pDocTemplate);/ 只有一个窗口m_pMainWnd-ShowWindow(SW_SHOW);m_pMainWnd-UpdateWindow( );return TRUE;/多文档应用程序的InitInstance( )函数BOOL CEx01App:InitInstance( )/建立文档模板CMultiDocTemplate* pDocTemplate;pDocTe

54、mplate = new CMultiDocTemplate(IDR_EX01TYPE,RUNTIME_CLASS(CEx01Doc),RUNTIME_CLASS(CChildFrame), / custom MDI child frameRUNTIME_CLASS(CEx01View);AddDocTemplate(pDocTemplate);/ 建立MDI主窗口框架CMainFrame* pMainFrame = new CMainFrame;if (!pMainFrame-LoadFrame(IDR_MAINFRAME)return FALSE;m_pMainWnd = pMainFra

55、me;/ 显示主窗口pMainFrame-ShowWindow(m_nCmdShow);pMainFrame-UpdateWindow( ); return TRUE; 下面是建立一个多文档应用程序的例子。操作如下: (1) 利用MFC Appwizard创建多文档的应用程序Ex04。 (2) 在项目工作区的类视图中双击CEx04Doc图标,出现类编辑器。在CEx04Doc的类声明中加入对m_Message的声明:class CEx04Doc : public CDocumentprotected: / create from serialization onlyCEx04Doc( );DEC

56、LARE_DYNCREATE(CEx04Doc)CString m_Message;(3) 修改CEx04Doc( )构造函数,对m_Message变量初始化:CEx04Doc:CEx04Doc( )/ TODO: add one-time construction code herem_Message=; (4) 按下Ctrl+W,打开ClassWizard的Message Maps选项卡,对CEx04View类的WM_CHAR消息添加消息处理函数OnChar,对其编辑如下:void CEx04View:OnChar(UINT nChar, UINT nRepCnt, UINT nFlags

57、) / TODO: Add your message handler code here and/or call defaultCEx04Doc* pDoc=GetDocument( );ASSERT_VALID(pDoc);pDoc-m_Message += nChar;Invalidate( );CView:OnChar(nChar, nRepCnt, nFlags);(5) 对OnDraw函数进行修改:void CEx04View:OnDraw(CDC* pDC)CEx04Doc* pDoc = GetDocument( );ASSERT_VALID(pDoc);/ TODO: add

58、draw code for native data herepDC-TextOut(0, 0, pDoc-m_Message); 编译并运行该程序,选择“文件”菜单中的“新建”,或直接在工具栏中按下“新建”按钮,创建多个文档窗口,在不同的窗口中可以输入并显示不同的字符串。10.3 对话框与常用组件对话框与常用组件 10.3.1 对话框对话框 1有模式对话框和无模式对话框有模式对话框和无模式对话框 在Windows应用程序中,对话框无处不在。在打开文件、查询以及其它数据交互时,都会用到对话框。从最简单的消息框,到复杂的数据处理框,都可以用对话框来完成。其实对话框是一个真正的窗口,它不但可以接受消

59、息,而且还可以被移动和关闭,甚至可以在它的客户区中进行绘图操作。更为便利的是,在设计时可以把控件直接粘到对话框上去,以实现各种操作。 对话框按其动作模式分为“有模式”和“无模式”两大类。有模式对话框在被关闭之前,用户无法再进行其它工作;无模式对话框被关闭之前,用户可以在应用程序的其它窗口中进行工作。当有模式对话框被打开之后,它就接管了父窗口的输入控制权,只有当用户关闭了该对话框之后,控制权才交给父窗口。而无模式对话框则与父窗口共享控制权,用户可以在主窗口和对话框之间来回切换。 根据有模式对话框的特点,它通常被用作输入数据,如常见的打开文件对话框、保存文件对话框、显示程序信息对话框等等。而无模式

60、对话框常用来提供更多的选择功能,如工具箱和调色板等。 2在在Visual C+程序中使用有模式对话框程序中使用有模式对话框 在Visual C+中,对话框同样也是程序的一项资源。下面以一个简单的例子,来介绍如何在应用程序中使用有模式对话框,在对话框中加入控件以及访问对话框中的数据。 首先建立一个SDI界面的应用程序Ex06,然后编辑对话框资源,步骤如下: (1) 在Insert菜单中选择Resource菜单项或直接按下Ctrl+R加速键,打开Insert Resource对话框,如图10.27所示。 (2) 在对话框中选取Dialog图标,然后单击New按钮,这时将建立一个新的对话框,并打开对话框编辑器,如图10.28所示。图10.27

温馨提示

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

评论

0/150

提交评论