Download presentation
Presentation is loading. Please wait.
Published byΑμφιτρίτη Βιτάλη Modified 6年之前
1
第三章对话框和常用控件 3.1 对话框的使用 3.2 控件的创建和使用方法 3.3 常用控件 3.4 通用对话框和消息对话框
3.1 对话框的使用 3.2 控件的创建和使用方法 3.3 常用控件 3.4 通用对话框和消息对话框 3.5 综合应用
2
3.1对话框的使用 在Visual C++ 6.0应用程序中,使用一个对话框通常有两种情形:一是直接创建一个对话框应用程序,二是在一个应用程序中进行调用。为不失一般性,这里介绍第二种对话框使用情况的一般过程,即依次如下: (1) 添加对话框资源; (2) 设置对话框的属性; (3) 添加和布局控件; (4) 创建对话框类; (5) 添加对话框代码; (6) 在程序中调用对话框。 显然,对于第一种对话框使用情况来说,其过程通常是从(2)到(5)。 添加对话框资源 在对话框资源添加之前,先来创建一个默认的单文档应用程序Ex_SDI: (1)在“D:\Visual C++应用”文件夹中,创建本章应用程序工作文件夹“第3章”。 (2)启动Visual C++ 6.0,选择“文件”→“新建”菜单,在弹出的“新建”对话框的工程标签页面中,选择MFC AppWizard(exe)的项目类型。单击位置框右侧的按钮,在弹出的对话框中,将该应用程序的文件夹定位在“D:\Visual C++应用\第3章”,并在工程编辑框中输入单文档应用程序名Ex_SDI。
3
3.1对话框的使用 (3)单击[确定]按钮,在出现的“步骤1”对话框中,选择“单个文档”应用程序类型。
(4)单击[完成]按钮出现一个信息对话框,显示出用户在前面几个步骤中作出的选择内容,单击[确定]按钮系统开始创建,并又回到了Visual C++ 6.0的主界面。 若无特别说明,以后凡遇到“创建一个默认的单文档应用程序Ex_XXXX”就是指上述的步骤,本书作此约定。其中,Ex_XXXX为创建的应用程序名。 这样就可在单文档应用程序Ex_SDI中添加一个对话框资源,通常按下列步骤进行: (1)选择“插入”→“资源”菜单,或按快捷键Ctrl+R打开“插入资源”对话框,在对话框中可以看到资源列表中存在Dialog项,若单击Dialog项左边的“+”号,将展开对话框资源的不同类型选项,如图3.1所示,表3.1列出各种类型的对话框资源的不同用途。 图3.1 “插入资源”对话框 其中,[新建]按钮是用来创建一个由“资源类型”列表中指定类型的新资源,[自定义]按钮是用来创建“资源类型”列表中没有的新类型的资源,[引入]按钮是用于将外部已有的位图、图标、光标或其他定制的资源添加到当前应用程序中。
4
3.1对话框的使用 表3.1 对话框资源类型 类 型 说 明 IDD_DIALOGBAR 对话条,往往和工具条停放一起
表3.1 对话框资源类型 类 型 说 明 IDD_DIALOGBAR 对话条,往往和工具条停放一起 IDD_FORMVIEW 一个表单(一种样式的对话框),用于表单视图类的资源模板 IDD_OLE_PROPPAGE_LARGE 一个大的OLE属性页 IDD_OLE_PROPPAGE_SMALL 一个小的OLE属性页 IDD_ PROPPAGE_LARGE 一个大属性页,用于属性对话框 IDD_ PROPPAGE_MEDIUM 一个中等大小的属性页,用于属性对话框 IDD_ PROPPAGE_SMALL 一个小的属性页,用于属性对话框
5
3.1对话框的使用 (3)对展开的不同类型的对话框资源不作任何选择,选中“Dialog”,单击[新建]按钮,系统就会自动为当前应用程序添加了一个对话框资源,并出现如图3.2所示的界面。 对话框模板 控件工具栏 布局工具栏 默认标识符 图3.2 添加对话框资源后的开发环境
6
3.1对话框的使用 ● 系统为对话框资源自动赋给它一个默认的标识符名称(第一次为IDD_DIALOG1,以后依次为IDD_DIALOG2、IDD_DIALOG3、...)。 ● 使用了通用的对话框模板创建新的对话框资源。对话框的默认标题为“对话” (Dialog),有[确定]和[取消]两个按钮,这两个按钮的标识符分别为IDOK和IDCANCEL。 ● 自动出现“对话框资源编辑器”窗口,在这里可以通过“控件工具栏”和“布局工具栏”向对话框添加和布局控件,并可设置对话框的属性。 需要说明的是,第一次使用Visual C++6.0的对话框编辑器时,“控件工具栏”和“布局工具栏”可能是浮动,可以将其拖放到指定位置(参看图3.2),以便让开发环境更易于操作。 设置对话框属性 在对话框模板的空白处右击鼠标,从弹出的快捷菜单中选择“属性”菜单项,出现如图3.3所示的对话框属性窗口。 可以看出,对话框具有这几类属性:常规(General)、样式(Styles)、更多样式(More Styles)、扩展样式(Extended Styles)、更多扩展More Extended Styles(更多扩展样式)等部分,这里仅介绍最常用的常规(General)属性,如表4.2所示。
7
3.1对话框的使用 图3.3 对话框属性窗口 表3.2 对话框的常规(General)属性 项 目 说 明 ID框
图3.3 对话框属性窗口 表3.2 对话框的常规(General)属性 项 目 说 明 ID框 修改或选择对话框的标识符名称 标题框 输入对话框的标题名称,中英文均可,如“我的对话框” 字体按钮 单击此按钮可选择字体的种类(如宋体)及尺寸(如9号) 位置X/位置Y 对话框左上角在父窗口中的X、Y坐标,都为0时表示居中 菜单框 默认值为无,当对话框需要菜单时输入或选择指定的菜单资源 类名称框 默认值为无,它提供C/C++语言编程时所需要的对话框类名,对MFC类库的资源文件来说,该项不被激活。
8
3.1对话框的使用 将添加的对话框的属性进行以下3点修改,结果如图3.4所示:
图3.3中,有一个 图标,当单击此图标后,图标变成,表示该对话框将一直显示直到用户关闭它。在 状态下,当该对话框一旦失去活动状态后就会自动消失! 将添加的对话框的属性进行以下3点修改,结果如图3.4所示: ● 在ID框中,将对话框标识符IDD_DIALOG1改成IDD_DIALOG_FIRST; ● 在标题框中,将对话框标题“对话”改为“我的第一个对话框”; ● 单击[字体]按钮,通过弹出的字体对话框将对话框内的文本设置成“宋体,9” ,以使自己的对话框和Windows中的对话框保持外观上的一致(这是界面设计的“一致性”原则)。
9
3.1对话框的使用 图3.4 对话框属性修改后的界面
10
3.1对话框的使用 添加和布局控件 一旦对话框资源被打开或被创建,就会出现对话框编辑器,通过它可以在对话框中进行控件的添加和布局等操作。 1. 控件的添加 对话框编辑器一旦打开,“控件”工具栏一般都会随之出现。若不出现,则可在开发环境的 工具栏区的空白处右击鼠标,从弹出的快捷菜单中选择“控件”。 利用“控件”工具栏中的各个按钮可以顺利完成控件的添加。图3.5说明了各个按钮所对应的控件类型。 控件的选择 静态文本 组框 复选框 组合框 水平滚动条 旋转按钮 滑动条 列表视图 标签 复合编辑 月历 用户定制控件 静态图片 编辑框 按钮 单选框 列表框 垂直滚动条 进展条 热键 树形视图 动画 日期选择 IP地址 扩展组合框 图3.5 控件工具栏和各按钮含义
11
3.1对话框的使用 向对话框添加一个控件的方法有下列几种:
● 在控件工具栏中单击某控件,此时的鼠标箭头在对话框内变成“十”字形状;在对话框指定位置单击鼠标左键,则此控件被添加到对话框的相应位置,再拖动刚添加控件的选择框可改变其大小和位置。 ● 在控件工具栏中单击某控件,此时的鼠标箭头对话框内变成“十”字形状;在指定位置处单击鼠标左键不放,拖动鼠标至满意位置,释放鼠标键。 ● 用鼠标左键点中控件工具栏中的某控件,并按住鼠标左键不放;在移动鼠标到对话框的指定位置的过程中,用户会看到一个虚线框,下面带有该控件的标记;释放鼠标左键,新添加的控件立即出现在对话框中。 2. 控件的选取 控件的删除、复制和布局操作一般都要先选取控件,若选取单个控件,则可以下列方法: ● 用鼠标直接选取。首先保证在控件工具栏中的选择按钮()是被选中的,然后移动鼠标指针至指定的控件上,单击鼠标左键即可。 ● 用助记符来选取。如果控件的标题中带有下划线的字符,这个字符就是助记符,选择时直接按下该助记符键或“Alt+助记符”组合键即可。
12
3.1对话框的使用 ● 用Tab键选取。在对话框编辑器中,系统会根据控件的添加次序自动设置相应的Tab键次序。利用Tab键,用户可在对话框内的控件中进行选择。每按一次Tab键依次 选取对话框中的下一个控件,若按住Shift键,再单击Tab键则选取上一个控件。 对于多个控件的选取,可采用下列方法: ● 先在对话框内按住鼠标左键不放,拖出一个大的虚框,然后释放鼠标,则被该虚框所包围的控件都将被选取。 ● 先按住Shift键不放,然后用鼠标选取控件,直到所需要的多个控件选取之后再释放Shift键。若在选取时,对已选取的控件再选取一下,则取消该控件选取。 需要注意的是: (1) 一旦单个控件被选取后,其四周由选择框包围着,选择框上还有几个(通常是八个)蓝色实心小方块,拖动它可改变控件的大小,如图3.6(a)所示。 (2)多个控件被选取后,其中只有一个控件的选择框有几个蓝色实心小方块,这个控件称为主要控件,而其他控件的选择框的小方块是空心的。如图3.6(b)所示。 图3.6 单个控件和多个控件的选择框 (a) (b)
13
3.1对话框的使用 3. 控件的删除、复制和布局 当单个控件或多个控件被选取后,按方向键或用鼠标拖动控件的选择框可移动控件。若在鼠标拖动过程中还按住Ctrl键则复制控件。若按Del键可将选取的控件删除。当然还有其他一些编辑操作,但这些操作方法和一般的文档编辑器基本相同,这里不再重复。 对于控件的布局,对话框编辑器中提供了控件布局工具栏,如图3.7所示,它可以自动地排列对话框内的控件,并能改变控件的大小。 靠齐上边 左右间隔相等 大小相同 靠齐左边 上下居中 宽度相同 显示标尺 靠齐右边 左右居中 高度相同 测试对话框 靠齐下边 上下间隔相等 显示网格 图3.7 控件布局工具栏
14
3.1对话框的使用 需要说明的是: (1) 随着对话框编辑器的打开,Visual C++ 6.0开发环境的菜单栏还出现“布局”菜单,它的命令与布局工具相对应,而且大部分命令名后面还显示出相应的快捷键,由于它们都是中文的(汉化过),故这里不再列出。 (2) 大多数布置控件的命令使用前,都需要用户选取多个控件,且“主要控件”起到了关键作用。例如用户选取多个控件后,使用“大小相同”命令只改变其它控件的大小,并与“主 要控件”的尺寸一致。因此,在多个控件的布置过程中,常需要重新设置“主要控件”。设置的方法是按住Ctrl或Shift键,然后用鼠标单击所要指定的控件即可。 (3) 为了便于用户在对话框内精确定位各个控件,系统还提供了网格、标尺等辅助工具。在图3.7的控件布局工具栏的最后两个按钮分别用来网格和标尺的切换。一旦网格显示,添加或移动控件时都将自动定位在网格线上。
15
3.1对话框的使用 4. 测试对话框 “布局”菜单下的“测试”命令或布局工具栏上的测试按钮是用来模拟所编辑的对话框的运行情况,帮助用户检验对话框是否符合用户的设计要求以及控件功能是否有效等。 5. 操作示例 (1) 下面来向对话框添加三个静态文本控件(一个静态文本控件就是一个文本标签): (2) 在控件工具栏上,单击 按钮,然后在对话框模板左上角单击鼠标左键不放,拖动 鼠标至满意位置,释放鼠标键。这样,第一个静态文本控件添加到对话框中了。 (3) 单击布局工具栏上的 按钮,打开对话框模板的网格。 (4) 在控件工具栏上,将 按钮拖放到对话框模板中的左中部。这样,第二个静态文本控件添加到对话框中了。同样的操作,将第三个静态文本控件拖放到对话框模板中的左下部。 (5) 按住Shift键不放,依次单击刚才添加的三个静态文本控件,结果如图3.8所示。 在布局工具栏上,依次单击“大小相同”按钮 、“靠左对齐”按钮 、“上下间隔相等”按钮 ,结果如图3.9所示。
16
图3.8 布局前的静态文本控件 图3.9 布局后的静态文本控件
3.1对话框的使用 图3.8 布局前的静态文本控件 图3.9 布局后的静态文本控件 创建对话框类 在对话框资源模板的空白区域(没有其他元素或控件)内双击鼠标或按Ctrl+W快捷键,将弹出一个对话框,询问是否为对话框资源创建一个新类。 单击[OK]按钮,将弹出如图3.10所示的New Class(新类)对话框。在Name(名称)框中输入类名CFirstDlg。Base class(基类)和Dialog ID(对话框标识符)内容是由系统自动设置的,一般无需修改。从Base class框的内容可以看出,用户对话框类是从基类CDialog派生而来的。
17
3.1对话框的使用 单击[OK]按钮,一个基于对话框资源模板的对话框类CFirstDlg就创建好了。此时,出现MFC ClassWizard(MFC类向导)对话框。 图3.10 “New Class”对话框
18
图3.11 “MFC ClassWizard”对话框
3.1对话框的使用 添加对话框代码 在MFC ClassWizard对话框,查看“Class name”列表中是否选择了CFirstDlg,若不是,则在IDs列表中选择CFirstDlg。在Messages框中找到并选定WM_INITDIALOG消息,如图3.11。单击[Add Function]按钮或双击WM_INITDIALOG消息,MFC ClassWizard自动为CFirstDlg类添加OnInitDialog函数。 图3.11 “MFC ClassWizard”对话框
19
3.1对话框的使用 需要说明的是,WM_INITDIALOG是在对话框显示之前向父窗口发送的消息,由于建立了此消息和OnInitDialog函数的关联,系统在对话框显示之前就会调用此函数,因此常将对话框一些初始化代码添加到这个函数中。 在Member functions列表框中选择刚添加的OnInitDialog函数,单击[Edit Code]按钮(或直接在函数名双击鼠标),将自动出现该函数代码编辑窗口,在此函数中添加下列代码: BOOL CFirstDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here this->SetWindowText("修改标题"); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } 代码中,SetWindowText是CWnd的一个成员函数,用来设置窗口的文本内容。对于对话框来说,它设置的是对话框标题。
20
3.1对话框的使用 在程序中调用对话框 在程序中调用对话框,一般是通过映射事件的消息(如命令消息、鼠标消息、键盘消息等),在映射函数中进行调用。这样,相应事件产生后,就会调用其消息映射函数,从而调用对话框的代码初执行。例如,下面的步骤用来实现在单文档应用程序 Ex_SDI的客户区中单击鼠标左键,显示前面添加的对话框。 (1) 按Ctrl+W键,弹出MFC ClassWizard对话框。 (2) 在Message Maps页面中,从Class name列表中选择CEx_SDIView,在IDs列表中选择CEx_SDIView,然后在Messages框中找到并选中WM_LBUTTONDOWN消息。 (3) 单击[Add Function]按钮或双击WM_LBUTTONDOWN消息,则该消息的映射函数OnLButtonDown自动添加到Member Functions列表框中。 (4) 选中刚才添加的OnLButtonDown函数,单击[Edit Code]按钮(或直接双击函数名),在打开的文档窗口中的此成员函数中添加下列代码: void CEx_SDIView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CFirstDlg dlg; dlg.DoModal(); CView::OnLButtonDown(nFlags, point); }
21
3.1对话框的使用 代码中,DoModal是CDialog基类成员函数,用来将对话框按模式方式来显示。
(5) 在CEx_SDIView 类的实现文件Ex_SDIView.cpp的前面添加CFirstDlg类的包含语句,即: #include "Ex_SDIDoc.h" #include "Ex_SDIView.h" #include "FirstDlg.h" (6) 编译并运行。在应用程序文档窗口的客户区中单击鼠标,就会出现如图3.12的对话框,这个对话框就是刚才添加的对话框,对话框的标题是程序代码所指定的结果。 图3.12 Ex_SDI运行的结果
22
3.1对话框的使用 模式对话框和无模式对话框 上述通过DoModal成员函数来显示的对话框称为模式对话框,所谓“模式对话框”是指当对话框被弹出,用户必须在对话框中作出相应的操作,在退出对话框之前,对话框所在应用程序的其它操作不能继续执行。 模式对话框的应用范围较广,一般情况下,模式对话框会有[确定](OK)和[取消](Cancel)按钮。单出[确定]按钮,系统认定用户在对话框中的选择或输入有效,对话框退出;单击[取消]按钮,对话框中的选择或输入无效,对话框退出,程序恢复原有状态。 事实上,对话框还可以用“无模式”方式来显示,称为无模式对话框,所谓“无模式对话框”是指当对话框被弹出后,一直保留在屏幕上,用户可继续在对话框所在的应用程序中进行其它操作;当需要使用对话框时,只需象激活一般窗口一样单击对话框所在的区域即可激活。由于“无模式”方式还要涉及到其它一些编程工作,限于篇幅,这里不作讨论。
23
3.2控件的创建和使用方法 控件的创建方法 控件的创建方式有以下两种:一种是在对话框模板资源中用编辑器来指定控件,这就是说,将控件的父窗口指定为对话框,这样做的好处是明显的,因为当应用程序启动该对话框时,框架系统就会为对话框创建控件;而当对话框消失时,控件也随之清除。另一种方式是将控件看作是任一窗口的子窗口,并通过调用相应的Create函数来创建。 [例Ex_Edit] 使用编程方式来创建一个编辑框 (1) 启动Visual C++ 6.0,选择“文件”→“新建”菜单,在弹出的“新建”对话框的工程标签页面中,选择MFC AppWizard(exe)的项目类型。单击位置框右侧的按钮,在弹出的对话框中,将该应用程序的文件夹定位在“D:\Visual C++应用\第3章”,并在工程编辑框中输入单文档应用程序名Ex_Edit。 (2) 单击[确定]按钮,在出现的“步骤1”对话框中,选择“基本对话框”应用程序类型。 (3) 单击[完成]按钮出现一个信息对话框,显示出用户在前面几个步骤中作出的选择内容,单击[确定]按钮系统开始创建,并又回到了Visual C++ 6.0的主界面。 若无特别说明,以后凡遇到“创建一个默认的基于对话框应用程序Ex_XXXX”就是指上述的步骤,本书作此约定。其中,Ex_XXXX为创建的应用程序名。
24
3.2控件的创建和使用方法 (4) 将项目工作区切换到ClassView页面,展开Ex_Edit所有的类结点,右击CEx_EditDlg类名,从快捷菜单中选择Add Member Variable(添加成员变量),在出现的对话框中定义一个CEdit类对象m_wndEdit,通常以“m_”来作为变量的开头,表示“成员”(member)的意思。如图3.13所示,单击[确定]按钮。 图3.13 添加成员变量 在MFC中,每一个类型的控件都用相应的类来封装。如编辑框控件,它的类是CEdit,通过该类对象来访问其成员,从而实现编辑框的操作。
25
3.2控件的创建和使用方法 (5) 在项目工作区窗口的ClassView页面中,将CEx_EditDlg结点展开,双击OnInitDialog函数名,在该函数中添加下列代码(return true;语句之前添加): BOOL CEx_EditDlg::OnInitDialog() { CDialog::OnInitDialog(); … CRect rcClient; this->GetClientRect( rcClient ); rcClient.right -= 100; m_wndEdit.Create(ES_MULTILINE|WS_CHILD|WS_VISIBLE|WS_TABSTOP| WS_BORDER, rcClient, this, 201 ); // 创建 CFont *font = this->GetFont(); // 获取对话框的字体 m_wndEdit.SetFont(font); // 设置控件字体 return TRUE; // return TRUE unless you set the focus to a control }
26
3.2控件的创建和使用方法 分析和说明: ● 前面曾说过,由于OnInitDialog函数在对话框初始化时被调用,因此将对话框中的一些初始化代码都添加在此函数中。 ● 由于Windows操作系统使用的是图形界面,因此在MFC中,对于每种界面元素的几何大小和位置常使用CPoint类(点)、CSize类(大小)和CRect类(矩形)来描述(以后还会讨论)。代码中,GetClientRect 是对话框基类CWnd的成员函数,用来获取其客户区的位置和大小。对于对话框来说,其客户区是指除标题栏(如果有的话)和边框之外的那部分区域。为了使创建的编辑框占用对话框整个客户区,且又不能覆盖对话框右侧的[确定]和[取消]按钮,故将右边的位置在原来的位置上减去100。 ● 代码中,CEdit类成员函数Create用来创建编辑框控件,该函数第一个参数用来指定指定控件的样式(样式),其中ES_MULTILINE(以ES_开头的)是编辑框类封装的预定义样式,表示创建的编辑框可以输入多行文本,WS_CHILD(子窗口)、WS_VISIBLE(可见)、WS_TABSTOP(可用Tab键选择)、WS_BORDER(带有边框)等都是CWnd类封装的预定义窗口样式,它们都可以直接引用,当多个样式指定时,需要使用按位或运算符“|”来连接。第二个参数用来指定它在父窗口中的位置和大小,第三个参数用来指定父窗口指针,最后一个参数是指定该控件的标识值。
27
3.2控件的创建和使用方法 ● 由于编辑框是作为对话框的一个子窗口来创建的,因此WS_CHILD样式是必不可少的,且还要使用WS_VISIBLE使控件在创建后显示出来。 (6) 编译并运行,这样就可以在创建的编辑框中输入文本了,结果如图3.14所示。 图3.14 Ex_Edit运行结果
28
3.2控件的创建和使用方法 3.2.2 控件的消息及消息映射 1. 控件的消息
控件的消息及消息映射 1. 控件的消息 当控件的状态发生改变时,控件就会向其父窗口发送消息,这个消息称为通知消息。对于一般控件来说,其通知消息通常是一条WM_COMMAND消息,这条消息的wParam参数的低位字中含有控件标识符,wParam参数的高位字则为通知代码,lParam参数则是指向控件的句柄。 而对于有些控件,其通知消息通常是一条WM_NOTIFY消息,这条消息的wParam参数是发送通知消息的控件的标识符,而lParam参数则是指向一个结构指针。 2. 映射控件消息 不管是什么控件消息,一般都可以用MFC ClassWizard对它们加以映射。例如: [例Ex_Btns] 一个或多个按钮控件的映射 (1) 创建一个默认的基于对话框应用程序Ex_Btns。 (2) 在打开的对话框资源模板中,删除“TODO: 在这里设置对话控制。”静态文本控件,将[确定]和[取消]按钮向对话框左边移动一段位置,然后将鼠标移至对话框资源模板右下角的实心蓝色方块处,拖动鼠标,将对话框资源模板的大小缩小一些。
29
3.2控件的创建和使用方法 (3) 在对话框资源模板的左边依次添加三个按钮控件,保留其默认属性,并将其布局得整齐一些,且上下间隔相同。如图3.15所示。 (4)按快捷键Ctrl+W,打开MFC ClassWizard对话框,查看“Class name”列表中是否选择了CEx_BtnsDlg,在IDs列表中选择 IDC_BUTTON1,这是添加第一个按钮后,系统自动为此按钮设置的默认标识符,然后在Messages框中选择BN_CLICKED消息。 图3.15 添加三个按钮 (5) 单击[Add Function]按钮或双击BN_CLICKED消息,出现“Add Member Function”对话框,在这里可以输入成员函数的名称,系统默认的函数名为OnButton1。如图3.16所示。
30
3.2控件的创建和使用方法 图3.16 添加按钮消息映射函数
31
3.2控件的创建和使用方法 需要说明的是: ● 不同资源对象(控件、菜单命令等)所产生的消息是不相同的。例如,按钮控件IDC_BUTTON1的消息有两个:BN_CLICKED和BN_DOUBLECLICKED,分别表示当用户单击或双击该按钮时产生的通知消息。 ● 一般不需要对对话框中的[确定](OK)与[取消](Cancel)按钮进行消息映射,因为系统已自动设置了这两个按钮的动作,当用户单击这两个按钮都将自动关闭对话框,且[确定](OK)按钮动作还使得对话框数据有效。 (6) 单击[OK]按钮,在MFC ClassWizard的“Member functions”列表中将列出新增加的成员函数。选择此函数,单击[Edit Code]按钮(或直接在函数名双击鼠标),开发环境的文档窗口中将自动打开该函数所在的源代码文件,并定位到该函数的实现代码处。在此成员函数中 添加下列代码: void CEx_BtnsDlg::OnButton1() { MessageBox("你按下了\"Button1\"按钮!"); }
32
3.2控件的创建和使用方法 (7) 编译并运行,当单击[Button1]按钮时,就会执行OnButton1函数,弹出一个消息对话框,显示“你按下了Button1按钮”。 这就是按钮BN_CLICKED消息的映射过程,其他控件的消息也可以类似映射。 3. 映射控件通用消息 上述过程是映射一个控件的某一个消息,事实上也可通过WM_COMMAND消息的映射来处理一个或多个控件的通用消息,如下面的步骤: (1) 打开MFC ClassWizard对话框,在“Class name”列表中是否选择了CEx_BtnsDlg,在IDs列表中选择CEx_BtnsDlg,在Messages框中找到并双击OnCommand,这样OnCommand消息函数就添加好了,如图3.17所示。由于OnCommand函数是一个用来处理WM_COMMAND消息的虚函数,而这里添加的OnCommand函数事实上是一个在类中实际调用的函数,可称为实例函数。这样的映射操作,可以称之为对虚函数OnCommand的重载。
33
3.2控件的创建和使用方法 图3.17 添加OnCommand函数重载
34
3.2控件的创建和使用方法 (2) 单击[Edit Code]按钮(或直接双击函数名),在OnCommand函数中添加下列代码:
BOOL CEx_BtnsDlg::OnCommand(WPARAM wParam, LPARAM lParam) { WORD nCode = HIWORD(wParam); // 控件的通知消息 WORD nID = LOWORD(wParam); // 控件的ID值 if ((nID == IDC_BUTTON1)&&(nCode == BN_CLICKED)) MessageBox("这是在OnCommand处理Button1的结果!"); if ((nID == IDC_BUTTON2)&&(nCode == BN_CLICKED)) MessageBox("这是在OnCommand处理Button2的结果!"); if ((nID == IDC_BUTTON3)&&(nCode == BN_CLICKED)) MessageBox("这是在OnCommand处理Button3的结果!"); return CDialog::OnCommand(wParam, lParam); }
35
3.2控件的创建和使用方法 (3) 编译运行并测试。
(3) 编译运行并测试。 需要说明的是:由于[Button1]按钮的BN_CLICKED消息处理同时存在两种函数,即OnButton1和OnCommand,因此若单击[Button1]按钮,系统会先执行哪一个函数呢?测试的结果表明,系统先执行OnCommand函数代码,然后执行OnButton1函数代码。之所 以还能执行OnButton1函数代码,是因为OnCommand函数的最后一句代码“return CDialog::OnCommand(wParam, lParam);”,它将控件的消息交由对话框其他函数处理。 控件的数据交换(DDX)和数据校验(DDV) 使用MFC ClassWizard可以很方便地为一个控件设置相关联变量并可设置其数据范围。例如,设一对话框中有一个编辑框和一个按钮控件,单击按钮控件,则按钮控件的标题就是编辑框中输入的内容。(这个示例为3部分阐述:控件及控件变量的添加、理解DDX/DDV、使用UpdateData) [例Ex_DDX] 控件的DDX和DDV 1. 控件及控件变量的添加 (1) 创建一个默认的基于对话框应用程序Ex_DDX。 (2) 在打开的对话框资源模板中,删除“TODO: 在这里设置对话控制。”静态文本控件,将[确定]和[取消]按钮向对话框左边移动一段位置,然后将鼠标移至对话框资源模板右下角的实心蓝色方块处,拖动鼠标,将对话框资源模板的大小缩小一些。 图3.18 添加编辑框和按钮
36
图3.19 “Member Variables”页面
3.2控件的创建和使用方法 (3) 在对话框资源模板的左边添加一个编辑框控件和一个按钮控件,保留其默认属性,并将其布局得整齐一些。如图3.18所示。 (4) 按快捷键Ctrl+W,打开MFC ClassWizard对话框,并切换到Member Variables页面,查看“Class name”列表中是否选择了CEx_DDXDlg,此时可以在Control IDs列表中看到刚才添加的控钮和编辑框的标识符IDC_BUTTON1和IDC_EDIT1,如图3.19所示。 在Control IDs列表中,选定按钮控件标识符IDC_BUTTON1,双击鼠标左键或单击[Add Variable]按钮,弹出Add Member Variable对话框,如图3.20所示。 图3.19 “Member Variables”页面
37
图3.20 为按钮控件添加控件变量 图3.21 为编辑框控件添加控件变量
3.2控件的创建和使用方法 图3.20 为按钮控件添加控件变量 图3.21 为编辑框控件添加控件变量
38
3.2控件的创建和使用方法 (6) 在Member variable name框中填好与控件相关联的成员变量m_RelBtn,单击[OK]按钮,又回到MFC ClassWizard对话框的Member Variables页面中,在Control IDs列表中出现刚才添加的控件变量m_RelBtn。 (7) 在Control IDs列表中,选定编辑框控件标识符IDC_EDIT1,双击鼠标左键或单击[Add Variable]按钮,弹出Add Member Variable对话框。在Member variable name框中输入控件变量名m_strEdit,保留Category的默认选项Value,在Variable type(变量值类型)中选择该变量的数据类型为CString(字符串类),结果如图3.21所示。 (8) 单击[OK]按钮,又回到MFC ClassWizard对话框的Member Variables页面。 从图3.20和3.21的不同可以看出: ● 对于大多数控件而言,Category框内可选择Value或Control两种类型。Control所对应的变量类型就是MFC为该控件封装的控件类,Value所对应的是数值类型。但按钮控件变量没有Value类型。 ● 不同的控件所提供的关联的数值类型各不相同。例如,对于编辑框来说,Variables type中的数值类型可以有CString(字符串类)、int、UINT、long、DWORD、float、double、BYTE、short、BOOL等。
39
3.2控件的创建和使用方法 ● 但要注意:DDV/DDX技术中,允许为同一个控件关联多个变量,但必须保证这些变量名是互不相同的,且这些变量在同一个类型不能有多个变量,即在Value和Control类型中各自只能有一个关联变量。另外,通常将Value型控件变量称为控件值变量。 ● 如果添加的关联变量是一个数值类型,则在MFC ClassWizard对话框的Member Variables页面下方还要求用户输入变量的范围,这就是控件的数据校验设置。 ● 在Control IDs列表中,选定编辑框控件标识符IDC_EDIT1,此时就会下方位置处出现Maximum Characters(最大字符个数)框,用来设定该控件变量允许的最大字符个数。输入20,结果如图3.22所示,单击[确定]按钮。 图3.22 编辑框控件值变量的DDV设置
40
3.2控件的创建和使用方法 需要说明的是,若控件值变量的数据类型是整型或浮点型,则DDV框就变成设置最大和最小的两个数值框了。
2. 理解DDX/DDV 打开CEx_DDXDlg类源文件,可以发现MFC ClassWizard对上述操作作了以下三方面的修改: (1) 在Ex_DDXDlg.h文件中,添加了控件变量的声明,代码如下面的加粗斜体部分: // Dialog Data //{{AFX_DATA(CEx_DDXDlg) enum { IDD = IDD_EX_DDX_DIALOG }; // 用枚举定义一个符号常量IDD,使其值等于IDD_EX_DDX_DIALOG CButtonm_RelBtn; CStringm_strEdit; //}}AFX_DATA (2) 在Ex_DDXDlg.cpp文件中的CEx_DDXDlg构造函数实现代码处,添加了控件变量的一些初始代码(加粗斜体部分):
41
3.2控件的创建和使用方法 CEx_DDXDlg::CEx_DDXDlg(CWnd* pParent /*=NULL*/)
: CDialog(CEx_DDXDlg::IDD, pParent) { //{{AFX_DATA_INIT(CEx_DDXDlg) m_strEdit = _T(""); //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 … } 在Ex_DDXDlg.cpp文件中的DoDataExchange函数体内,添加了控件的DDX/DDV代码(加粗斜体部分),它们都是一些以DDV_或DDX_开头的函数调用。 void CEx_DDXDlg::DoDataExchange(CDataExchange* pDX) CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CEx_DDXDlg) DDX_Control(pDX, IDC_BUTTON1, m_RelBtn); DDX_Text(pDX, IDC_EDIT1, m_strEdit); DDV_MaxChars(pDX, m_strEdit, 20); //}}AFX_DATA_MAP
42
3.2控件的创建和使用方法 需要说明的是,上述代码中以“//{{AFX_DATA”或“//{{AFX_DATA_XXXX”开头,而以“//}}AFX_DATA”或“//}}AFX_DATA_XXXX”结尾的标记是ClassWizard定义的专门用作DDX/DDV的标记,表示该部分的代码是由ClassWizard自动管理,一般不需要更改。 3. 使用UpdateData 当为一个控件定义一个关联的数据变量后,即对控件值变量来说,就可使用CWnd类的成员函数UpdateData使数值在控件显示和控件值变量之间进行切换。 UpdateData函数只有一个为TRUE或FALSE的参数。当调用UpdateData(FALSE)时,数据由控件相关联的成员变量向控件传输,即将控件值变量的值在控件中显示出来;当调用UpdateData(TRUE)或不带参数的UpdateData时,数据从控件向相关联的 成员变量复制,即将当前控件显示的值存储到控件值变量中。故,当需要获取当前控 件的值之前,一定要调用UpdateData(TRUE)或不带参数的UpdateData。例如: (1) 按快捷键Ctrl+W,打开MFC ClassWizard对话框,为按钮IDC_BUTTON1在CEx_DDXDlg类添加BN_CLICKED消息的映射函数OnButton1。
43
3.2控件的创建和使用方法 在映射函数OnButton1中添加下面代码: void CEx_DDXDlg::OnButton1()
{ UpdateData(); // 默认参数值是TRUE m_strEdit.TrimLeft(); m_strEdit.TrimRight(); if ( m_strEdit.IsEmpty() ) m_RelBtn.SetWindowText( "Button1" ); else m_RelBtn.SetWindowText( m_strEdit ); } 代码中,由于m_strEdit 是CString类对象,因而可以调用CString类的公有成员函数。其中,TrimLeft和TrimRight函数不带参数时分别用来去除字符串最左边或最边一些空格符、换行符、Tab字符等白字符,IsEmpty是用来判断字符串是否为空。
44
3.2控件的创建和使用方法 这样,当编辑框内容有除白字符之外的实际字符的字符串时,SetWindowText用将其内容设定为按钮控件的标题。否则,按钮控件的标题为“Button1”。 (3) 编译并运行。当在编辑框中输入“Hello”后,单击[Button1]按钮,按钮的名称就变成了编辑框控件中的内容“Hello”,其结果如图3.23所示。 图3.23 Ex_DDX运行结果
45
3.3常用控件 根据控件的特性和功能,一般可将其分为三类:Windows公共控件、ActiveX控件以及MFC新增的一些控件等。表3.3列出了本书所用到的常用控件类。 表3.3 常用控件类 控件名称 MFC类 功能描述 静态控件 CStatic 用来显示一些几乎固定不变的文字或图形 按钮 CButton 用来产生某些命令或改变某些选项,包括单选按钮、复选框和组框 编辑框 CEdit 用于完成文本和数字的输入和编辑 列表框 CListBox 显示一个列表,让用户从中选取一个或多个项 组合框 CComboBox 是一个列表框和编辑框组合的控件 滚动条 CScrollBar 通过滚动块在滚动条上的移动和滚动按钮来改变某些量 进展条 CProgressCtrl 用来表示一个操作的进度 滑动条 CSliderCtrl 通过滑动块的移动来改变某些量,并带有刻度指示 旋转按钮控件 CSpinButtonCtrl 带有一对反向箭头的按钮,单击这对按钮可增加或减少某个值 日期时间控件 CDateTimeCtrl 用于选择指定的日期和时间 图像列表 CImageList 一个具有相同大小的图标或位图的集合 列表控件 CListCtrl 可以用 “大图标”、“小图标”、“列表”或“报表”等四种不同的方式来显示一组信息 树控件 CTreeCtrl 用树结构的形式显示一组信息,并能反映这些信息的层次关系
46
3.3常用控件 3.3.1 静态控件和按钮 静态控件和按钮是Windows最基本的控件之一。 1. 静态控件
静态控件和按钮 静态控件和按钮是Windows最基本的控件之一。 1. 静态控件 一个静态控件是用来显示一个字符串、框、矩形、图标、位图或增强的图元文件。它可以被用来作为标签、框或用来分隔其它的控件。一个静态控件一般不接收用户输入,也不产生通知消息。 在对话框编辑器的控件工具栏中,属于静态控件的有:静态文本( )、组框( )和静态图片( )三种。其中,静态图片控件的常规(General)属性对话框(右击添加的控件,从弹出的快捷菜单中选择“属性”菜单,即可弹出该控件的属性对话框),如图3.24所示。 在静态图片控件的常规属性对话框中,用户可以选择图片“类型”、“图像”两个组合框中的有关选项内容,并可将应用程序资源中的图标、位图等内容显示在该静态图片控件中。另外,用户还可设置其样式来改变控件的外观以及图像在控件的位置等。例如,在任一个对话框中添加一个静态图片控件,在其常规属性对话框中,将其“类型”选择为“图标(Icon)”,再将其“图像”选择为IDR_MAINFRAME,则静态图片控件显示的图标是 。
47
3.3常用控件 [例Ex_Etched] 制作水平蚀刻线 (1)创建一个默认的对话框应用程序Ex_Etched。
图3.24 静态图片控件的常规属性对话框 [例Ex_Etched] 制作水平蚀刻线 (1)创建一个默认的对话框应用程序Ex_Etched。 (2) 在打开的对话框资源模板中,删除“TODO: 在这里设置对话控制。”静态文本控件,将[确定]和[取消]按钮向对话框左边移动一段位置,然后将鼠标移至对话框资源模板右下角的实心蓝色方块处,拖动鼠标,将对话框资源模板的大小缩小一些。
48
3.3常用控件 (3)在对话框资源模板中的靠左中间位置添加一个静态图片控件,右击该控件,从弹出的快捷菜单中选择“属性”,弹出其属性对话框。
(4)将“类型”选择为默认的“框架(Frame)”类型,将“颜色”类型选为“蚀刻(Etched)”,然后关闭属性对话框。此时,静态图片控件变成一个蚀刻的矩形框。 将鼠标移动到添加的静态图片控件的右下角位置,使鼠标指针变成,拖动鼠标使控件的大小变成一条水平线,单击对话框测试按钮,则结果如图3.25所示。 水平蚀刻线 图3.25 水平蚀刻线 凡以后在对话框中有这样的水平蚀刻线或垂直蚀刻线,都是指的这种制作方法,书中不再讲述其制作过程。本书作此约定。
49
3.3常用控件 2. 按钮 在Windows中所用的按钮是用来实现一种开与关的输入,常见的按钮有3种类型:按键按钮、单选按钮、复选框按钮,如图3.26所示。 图3.26 按钮的不同类型 (1)按键按钮。按键按钮通常可以立即产生某个动作,执行某个命令,因此也常被称为命令按钮。按键按钮有两种样式:标准按键按钮和默认(缺省)按键按钮。从外观上来说,默认按键按钮是在标准按键按钮的周围加上一个黑色边框(参见图3.26),这个黑色边框表示该按钮已接受到键盘的输入焦点,这样一来,用户只须按回车键就能按下该按钮。一般来说,只把最常用的按键按钮设定为默认按键按钮,具体设定的方法是在按键按钮属性对话框的样式(Style)页面中选中“缺省按钮(Default button)”项。
50
3.3常用控件 (2)单选按钮。单选按钮的外形是在文本前有一个圆圈,当它被选中时,单选按钮中就标上一个黑点,它可分为一般和自动两种类型。在自动类型中,用户若选中同组按钮中的某个单选按钮,则其余的单选按钮的选中状态就会清除,保证了多个选项始终只有一个被选中。 (3)复选框。复选框的外形是在文本前有一个空心方框,当它被选中时,复选框 中就加上一个“”标记,通常复选框只有选中和未选中两种状态,若复选框前面有一个灰色是“”,则这样的复选框是三态复选框,如图3.26的Check2,它表示复选框的选择状态是“不确定”。设定成三态复选框的方法是在复选框属性对话框的样式(Style)页面中选中“三次状态(Tri-state)”项。 3. 按钮的消息 在按钮映射的消息中,常见的只有两个: BN_CLICKED(单击按钮)和BN_DOUBLE CLICKED(双击按钮)。 4. 按钮选中操作 最常用的按钮操作是设置或获取一个按钮或多个按钮的选中状态。CButton类的成员函数SetCheck和GetCheck分别用来设置或获取指定按钮的选中状态,其原型如下: void SetCheck( int nCheck ); int GetCheck( ) const;
51
3.3常用控件 其中,nCheck和GetCheck函数返回的值可以是:0表示不选中,1表示选中,2表示不确定(仅用于三态按钮)。
而对于同组多个单选按钮的选中状态的设置或获取,需要使用通用窗口类CWnd的成员函数CheckRadioButton和GetCheckedRadioButton,它们的原型如下: void CheckRadioButton( int nIDFirstButton, int nIDLastButton, int nIDCheckButton ); int GetCheckedRadioButton( int nIDFirstButton, int nIDLastButton ); 其中,nIDFirstButton和nIDLastButton分别指定同组单选按钮的第一个和最后一个按钮ID值,nIDCheckButton用来指定要设置选中状态的按钮ID值, 函数GetCheckedRadioButton返回被选中的按钮ID值。 5. 示例:制作问卷调查 问卷调查是日常生活中经常遇到的调查方式。例如,图3.27就是一个问卷调查对话框,它针对“上网”话题提出了三个问题,每个问题都有四个选项,除最后一个问题外,其余都是单项选择。创建的过程如下(分创建并设计对话框、完善代码两部分来阐述): 图3.27 上网问卷调查对话框
52
3.3常用控件 [例Ex_Research] 制作问卷调查 1. 创建并设计对话框
1. 创建并设计对话框 (1)创建一个默认的对话框应用程序Ex_Research。 (2)Visual C++会自动打开对话框编辑器并显示对话框资源模板。单击对话框编辑器工具栏上的切换网格按钮 ,显示对话框网格,将对话框标题改为“上网问卷调查”。 (3)调整对话框的大小,删除对话框中间的“TODO: 在这里设置对话控制。”静态文本控件,将[确定]和[取消]按钮移至对话框的下方,并向对话框中添加组框(Group)控件,然后参看图3.28调整其大小和位置。图3.28 添加的组框和单选按钮 (4)右击添加的组框控件,从弹出的快捷菜单中选择“属性”菜单,出现该控件的属性对话框,在属性对话框“常规(General)”页面中可以看到它的ID为默认的IDC_STATIC。将其标题(Caption)属性内容由“Static”改成“你的年龄”。在组框控件的“样式”(Styles)属性中,“水平排列”属性用来指定文本在顶部的左边(Left)、居中(Center)还是右边(Right)。保留其缺省(Default)选项,它表示左对齐。
53
3.3常用控件 (5)在组框内添加4个单选按钮,默认的ID依次为IDC_RADIO1、IDC_RADIO2、IDC_RADIO3和IDC_RADIO4。在其属性对话框中将ID属性内容分别改成IDC_AGE_L18、IDC_AGE_18T27、IDC_AGE_28T38和IDC_AGE_M38,然后将其“标题”(Caption)属性内容分别改成“< 18”、“ ”、“ ”和“> 38”,最后调整位置,结果如图3.28所示。 (6)接下来添加一个静态文本,标题设为“你使用的接入方式:”,然后在其下再添加4个单选按钮,标题分别是“FTTL或ADSL”、“单位LAN”、“拨号56K”和“其他”,并将相应的ID属性依次改成:IDC_CM_FTTL、IDC_CM_LAN、IDC_CM_56K和IDC_CM_OTHER。用对话框编辑器工具栏的按钮命令调整控件左右之间的间距,结果如图3.29所示。 图3.28 添加的组框和单选按钮
54
3.3常用控件 图3.29 再添加单选框图 图3.30 三个问题全部添加后的对话框
55
3.3常用控件 (7)在对话框的下方,再添加一个组框控件,其标题为“你上网主要是”。然后添加四个复选框,其标题分别为“收发邮件”、“浏览资料”、“聊天游戏”和“其他”,ID分别为IDC_DO_POP、IDC_DO_READ、IDC_DO_GAME和IDC_DO_OTHER。结果如图3.30所示。 (8)单击工具栏上的测试对话框按钮 。对话框测试后,可以发现:顺序添加的这8个单选按钮全部变成一组,也就是说,在这组中只有一个单选按钮被选中,这不符合本意。解决这个问题的最好的办法是将每一组中的第一个单选按钮的“组”(Group)属性选中。 (9)将第一和第二个问题中的第一个单选按钮的“组”(Group)属性均选中。 (10)单击对话框编辑器工具栏上的切换辅助线按钮 ,然后将对话框中的控件调整到辅助线以内,并适当对其他控件进行调整。这样,整个问卷调查的对话框就设计好了,单击工具栏上的测试对话框按钮 进行测试。 2)完善代码 (1)将项目工作区切换到ClassView(类视图)页面,展开CEx_ResearchDlg类的所有成员,双击OnInitDialog函数名就会在文档窗口中自动定位到该函数的实现代码处,在此函数添加下列初始化代码:
56
3.3常用控件 BOOL CEx_ResearchDlg::OnInitDialog()
{ CDialog::OnInitDialog(); … CheckRadioButton(IDC_AGE_L18, IDC_AGE_M38, IDC_AGE_18T27); CheckRadioButton(IDC_CM_FTTL, IDC_CM_OTHER, IDC_CM_FTTL); CButton* pBtn = (CButton*)GetDlgItem(IDC_DO_POP); pBtn->SetCheck(1); // 使“收发邮件”复选框选中 return TRUE; // return TRUE unless you set the focus to a control } 代码中,GetDlgItem是CWnd类的一个成员函数,用来获得对话框中控件(参数是控件的ID标识符)的窗口指针。 用MFC ClassWizard在CEx_ResearchDlg类中添加IDOK按钮的BN_CLICKED消息映射,并添加下列代码,使得用按[确定]按钮获取用户所作的选择内容。
57
3.3常用控件 代码中,GetDlgItemText是CWnd类成员函数,用来获得对话框(或其他窗口)中的指定控件的窗口文本。在单选按钮和复选框中,控件的窗口文本就是它们的标题属性内容。该函数有两个参数,第一个参数用来指定控件的标识,第二个参数是返回的窗口文本。后面的函数GetWindowText的作用与GetDlgItemText相同,也是获取窗口的文本内容。不过,GetWindowText使用更加广泛,但要注意 这两个函数在使用上的不同。 (3)编译并运行,出现“上网问卷调查”对话框, 当回答问题后,按[确定]按钮,出现如图3.31所示的消息 对话框,显示用户选择的内容。 可见,在组框、静态文本、单选按钮、复选框等控件 的混和使用时,常常需要通过CheckRadioButton函数来设 置同组单选按钮的最初选中状态, 通过SetCheck来设置指定复选框的选中状态,然后通过GetCheck和GetCheckedRadioButton来判断被选中的复选框和单选按钮,并通过GetDlgItemText 或GetWindowText获取选中控件的窗口文本。 图3.31 显示用户选择的内容
58
3.3常用控件 编辑框和旋转按钮控件 1. 编辑框 编辑框( )是一个让用户从键盘输入和编辑文本的矩形窗口,用户可以通过它,很方便地输入各种文本、数字或者口令,也可使用它来编辑和修改简单的文本内容。当编辑框具有输入焦点时,就会出现一个闪动的插入符(又称为文本光标),表明当前插入点的位置。 (1)编辑框的属性和通知消息 在控件的属性对话框中,“常规”属性页面通常用来设置控件的ID号、组、标题等属性,而“样式”属性页面反映了该控件的独有的主要的常用的样式。图3.32是编辑框的“样式”属性页面,表3.4列出其中各项的含义。 图3.32 编辑框的“样式”属性
59
3.3常用控件 表3.4 编辑框的“样式”属性 项 目 说 明 排列文本(Align text)
表3.4 编辑框的“样式”属性 项 目 说 明 排列文本(Align text) 文本对齐方式:靠左(Left)、居中(Center)、靠右(Right),默认时为靠左(Left) 多行(Multiline) 选中时为多行编辑框,否则为单行编辑框 数字(Number) 选中时控件只能输入数字 水平滚动(Horizontal scroll) 水平滚动,仅对多行编辑框有效。 自动水平滚动(Auto Hscroll) 当用户在行尾键入一个字符时,文本自动向右滚动。 垂直滚动(Vertical scroll) 垂直滚动,仅对多行编辑框有效 自动垂直滚动(Auto Vscroll) 当用户在最后一行按ENTER键时,文本自动向上滚动一页,仅对多行编辑框有效 密码(Password) 选中时,键入编辑框的字符都将显示为“*”,仅对单行编辑框有效 没有隐藏选项(No hide selection) 通常情况下,当编辑框失去键盘焦点时,被选择的文本仍然反色显示。选中时,则不具备此功能 OEM转换(OEM convert) 选中时,实现对特定字符集的字符转换 需要返回(Want return) 选中时,用户按下ENTER键,编辑框中就会插入一个回车符 边框(Border) 选中时,在控件的周围存在边框 大写(Uppercase) 选中时,键入在编辑框的字符全部转换成大写形式 小写(Lowercase) 选中时,键入在编辑框的字符全部转换成小写形式 只读(Read-Only) 选中时,防止用户键入或编辑文本
60
3.3常用控件 当编辑框的文本修改或者被滚动时,会向其父窗口发送一些消息,如表3.5所示。 表3.5 编辑框的通知消息 通知消息 说 明
表3.5 编辑框的通知消息 通知消息 说 明 EN_CHANGE 当编辑框中的文本已被修改,在新的文本显示之后发送此消息 EN_HSCROLL 当编辑框的水平滚动条被使用,在更新显示之前发送此消息 EN_KILLFOCUS 编辑框失去键盘输入焦点时发送此消息 EN_MAXTEXT 文本数目到达了限定值时发送此消息 EN_SETFOCUS 编辑框得到键盘输入焦点时发送此消息 EN_UPDATE 编辑框中的文本已被修改,新的文本显示之前发送此消息。 EN_VSCROLL 当编辑框的垂直滚动条被使用,在更新显示之前发送此消息。
61
3.3常用控件 (2) 编辑框的基本操作 由于编辑框的形式多样,用途各异,因此下面针对编辑框的不同用途,分别介绍一些常用操作,以实现一些基本功能。 ① 口令设置。口令设置在编辑框中不同于一般的文本编辑框,用户输入的每个字符都被一个特殊的字符代替显示,这个特殊的字符称为口令字符。默认的口令字符是“*”,应用程序可以用成员函数CEdit::SetPasswordChar 来定义自己的口令字符,其函数原型如下: void SetPasswordChar( TCHAR ch ); 其中,参数ch表示设定的口令字符;当ch = 0时,编辑框内将显示实际字符。 ② 选择文本。当在编辑框中编辑文本时,往往需要选定文本作为整体进行各种编辑操作。用户可以用鼠标或键盘来选择文本。用鼠标来选择文本的操作方法是:在要选择的文本的一端按下鼠标左键并拖动鼠标,到另一端释放鼠标键。用键盘来选择文本的方法是:在按光标方向移动键的同时,按住Shift键。 ③ 设置编辑框的页面边距。设置编辑框的页面边距可以使文本在编辑框显示更具满意效果,这在多行编辑框中尤为重要,应用程序可通过调用成员函数CEdit::SetMargins来实现,这个函数的原型如下: void SetMargins( UINT nLeft, UINT nRight ); 其中,参数nLeft和nRight分别用来指定左、右边距的像素大小。
62
3.3常用控件 ④ 剪帖板操作。编辑框通过CEdit类的Copy、Paste和Cut成员函数来实现文本的复制、粘贴、剪切的操作,并还自动支持键盘快捷操作,其对应的快捷健分别为Ctrl+C、Ctrl+V和Ctrl+X。若应用程序调用CEdit::Undo函数时,则还可撤消当前的操作,再调用一次该函数,则恢复刚才的操作。例如下面的代码: if (m_Edit.CanUndo()) m_Edit.Undo(); ⑤ 获取多行编辑框文本。获取多行编辑框控件的文本可以有两种方法: 一种是使用DDX/DDV,当将编辑框控件所关联的变量类型选定为CString后,则不管多行编辑框的文本有多少都可用此变量来保存,从而能简单地解决多行文本的读取。但这种方法不能单独获得多行编辑框中的某一行文本。 另一种方法是使用编辑框CEdit类的相关成员函数来获取文本。例如,下面的代码获取并将显示编辑框中第二行的文本内容: char str[100]; if (m_Edit.GetLineCount()>=2) { // 判断多行编辑框的文本是否有两行以上 int nChars; nChars = m_Edit.LineLength(m_Edit.LineIndex(1)); // 获取第二行文本的字符个数 // 0表示第一行,1表示第二行,依次类推。LineIndex用于将文本行转换成 // 能被LineLength识别的索引 m_Edit.GetLine(1,str,nChars); // 获取第二行文本 str[nChars] = '\0'; MessageBox(str); }
63
3.3常用控件 代码中,由于调用GetLine获得某行文本内容时,并不能自动在文本后添加文本的结束符‘\0’,因此需要首先获得某行文本的字符数,然后设置文本的结束符。 2. 旋转按钮控件 旋转按钮控件( ,也称为上下控件)是一对箭头按钮,用户点击它们来增加或减小某个值,比如一个滚动位置或显示在相应控件中的一个数字。如图3.33所示。 图3.33 旋转按钮控件及其伙伴窗口 旋转按钮 伙伴窗口 (1) 旋转按钮控件常用的样式。旋转按钮控件有许多样式,它们都可以通过旋转按钮控件属性对话框进行设置,如图3.34所示,其中各项的含义见表3.6。 图3.34 旋转按钮控件样式属性对话框
64
3.3常用控件 表3.6 旋转按钮控件的样式(Style)属性 项 目 说 明 方位(Orientation)
项 目 说 明 方位(Orientation) 控件放置方向:垂直(Vertical)、水平(Horizontal) 对齐(Alignment) 控件在伙伴窗口的位置安排:独立(Unattached)、靠右(Right)、靠左(Left) 自动结伴(Auto buddy) 选中此项,自动选择一个Z-order中的前一个窗口作为控件的伙伴窗口 自动结伴整数(Set buddy integer) 选中此项,使控件设置伙伴窗口数值,这个值可以是十进制或十六进制 没有上千(No thousands) 选中此项,不在每隔三个十进制数字的地方加上千分隔符 换行(Wrap) 选中此项,当增加或减小的数值超出范围,则从最小值或最大值开始回绕 箭头键(Arrow keys) 选中此项,当按下向上和向下方向键时,也能增加或减小 热轨迹(Hot track) 选中此项,当光标移过控件时,突出显示控件的上下按钮
65
3.3常用控件 (2)旋转按钮控件的基本操作。MFC的CSpinButtonCtrl类提供了旋转按钮控件的 各种操作函数,使用它们可以进行基数、范围、位置设置和获取等基本操作。 成员函数SetBase是用来设置其基数的,这个基数值决定了伙伴窗口显示的数字是十进制还是十六进制。如果成功则返回先前的基数值,如果给出的是一个无效的基数则返回一个非零值。函数的原型如下: int SetBase( int nBase ); 其中参数nBase表示控件的新的基数,如10表示十进制,16表示十六进制等。与此函数相对应的成员函数GetBase是获取旋转按钮控件的基数。 成员函数SetPos和SetRange分别用来设置旋转按钮控件的当前位置和范围,它们的函数原型如下: int SetPos( int nPos ); void SetRange( int nLower, int nUpper ); 其中,参数nPos表示控件的新位置,它必须在控件的上限和下限指定的范围之内。nLower 和 nUpper表示控件的上限和下限。任何一个界限值都不能大于0x7fff 或小于 -0x7fff 。与这两个函数相对应的成员函数GetPos和GetRange分别用来获取旋转按钮控件的当前位置和范围。 (3)旋转按钮控件的通知消息。旋转按钮控件的通知消息只有一个:UDN_DELTAPOS,它是在当控件的当前数值将要改变时向其父窗口发送的。
66
3.3常用控件 3. 示例:用对话框输入学生成绩 在一个简单的学生成绩结构中,常常有学生的姓名、学号以及三门成绩等内容。为了能够输入这些数据,需要设计一个对话框,如图3.35所示。 图3.35 学生成绩输入对话框 图3.36 设计的学生成绩输入对话框
67
3.3常用控件 [例Ex_Input] 用对话框输入学生成绩 1)创建并设计对话框
(1)用MFC AppWizard(exe)创建一个默认的单文档应用程序Ex_Input。 (2)添加一个新的对话框资源,通过其属性对话框,将ID标识符改为IDD_INPUT,标题为“学生成绩输入”,将对话框字体改为“宋体,9号”。 (3)显示对话框网格,调整对话框的大小,然后将[确定]和[取消]按钮移至对话框的下方。 (4)向对话框添加如表3.7所示的控件,调整控件的位置,结果如图3.36所示。 表3.7 学生成绩输入对话框添加的控件 添加的控件 ID标识符 标 题 其他属性 编辑框 IDC_EDIT_NAME —— 默认 IDC_EDIT_NO IDC_EDIT_S1 旋转按钮控件 IDC_SPIN_S1 自动结伴, 自动结伴整数、靠右对齐 IDC_EDIT_S2 IDC_SPIN_S2 IDC_EDIT_S3 IDC_SPIN_S3
68
3.3常用控件 需要说明的是,由于控件的添加、布局和设置属性的方法以前已详细阐述过,为了节约篇幅,这里用表格形式列出所要添加的控件,并且因默认ID标识符的静态文本控件的“标题”属性内容可从对话框直接看出,因此一般不在表中列出,本书作此约定。表格中ID、标题和其他属性均是通过控件的属性对话框进行设置的,凡是“默认”属性均为保留属性对话框中的默认设置。 (5)选择“布局”→“Tab次序”命令,或按快捷键Ctrl+D,此时每个控件的左上方都有一个数字,表明了当前Tab键次序,单击对话框中的控件,重 新设置控件的Tab键次序,以保证旋转按钮控件的 Tab键次序在相对应的编辑框(伙伴窗口)之后, 结果如图3.37所示,单击对话框或按Enter键结束“Tab次序”方式。 (6) 双击对话框模板空白处,为该对话框模板创建一个对话框类CInputDlg。 图3.37 改变控件的Tab键次序
69
3.3常用控件 2)完善CInputDlg类代码 (1)在MFC ClassWizard的Member Variables页面中,确定Class name中是否已选择了CInputDlg,选中所需的控件ID标识符,双击鼠标或单击Add Variables按钮。依次为表3.8控件增加成员变量。 表3.8 控件变量 控件ID标识符 变量类别 变量类型 变量名 范围和大小 IDC_EDIT_NAME Value CString m_strName 20 IDC_EDIT_NO m_strNO 20 IDC_EDIT_S1 float m_fScore1 0.0 ~ 100.0 IDC_SPIN_S1 Control CSpinButtonCtrl m_spinS1 —— IDC_EDIT_S2 m_fScore2 IDC_SPIN_S2 m_spinS2 IDC_EDIT_S3 m_fScore3 IDC_SPIN_S3 m_spinS3
70
3.3常用控件 (2)用MFC ClassWizard为CInputDlg添加WM_INITDIALOG消息映射,添加下列代码:
BOOL CInputDlg::OnInitDialog() { CDialog::OnInitDialog(); m_spinScore1.SetRange( 0, 100 ); // 设置旋转按钮控件范围 m_spinScore2.SetRange( 0, 100 ); m_spinScore3.SetRange( 0, 100 ); return TRUE; // return TRUE unless you set the focus to a control }
71
3.3常用控件 (3)用MFC ClassWizard为CInputDlg增加IDC_SPIN_S1控件的UDN_DELTAPOS消息映射,并添加下列代码: void CInputDlg::OnDeltaposSpinS1(NMHDR* pNMHDR, LRESULT* pResult) { NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; UpdateData(TRUE); // 将控件的内容保存到变量中 m_fScore1 += (float)pNMUpDown->iDelta * 0.5f; if (m_fScore1<0.0) m_fScore1 = 0.0f; if (m_fScore1>100.0) m_fScore1 = 100.0f; UpdateData(FALSE); // 将变量的内容显示在控件中 *pResult = 0; } 代码中,NM_UPDOWN结构用于反映旋转控件的当前位置(由成员iPos指定)和增量大小(由成员iDelta指定)。
72
3.3常用控件 3)调用对话框 用MFC ClassWizard为CEx_InputView类添加键按下消息WM_KEYDOWN的消息映射,并添加下列代码: void CEx_InputView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if ( nChar == VK_F7 ) // 按下F7功能键 CInputDlg dlg; if (IDOK == dlg.DoModal()) { // 获取对话框数据 CString str; str.Format("%s, %s, %4.1f, %4.1f, %4.1f", dlg.m_strName, dlg.m_strNO, dlg.m_fScore1, dlg.m_fScore2, dlg.m_fScore3 ); AfxMessageBox(str); } CView::OnKeyDown(nChar, nRepCnt, nFlags);
73
3.3常用控件 分析和说明: ● 此例对话框是通过映射键盘消息而调用的。当用户按下一个键或组合键时,Windows将WM_KEYDOWN或WM_SYSKEYDOWN放入具有输入焦点的应用程序窗口的消息队列中。当键被释放时,Windows则把WM_KEYUP或WM_SYSKEYUP消息放入消息队列中。对于字符键来说,还会在这两个消息之间产生WM_CHAR消息。 MFC ClassWizard能自动添加了当前类的WM_KEYDOWN和WM_KEYUP击键消息处理函数的调用,它们具有下列函数原型: afx_msg void OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags ); afx_msg void OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags ); a fx_msg是MFC用于定义消息函数的标志,参数nChar表示虚拟键代码,nRepCnt表示当用户按住一个键时的重复计数,nFlags表示击键消息标志。 所谓虚拟键代码,是指与设备无关的键盘编码。在Visual C++中,最常用的虚拟键代码已被定义在Winuser.h中,例如:VK_SHIFT表示SHIFT键,VK_F1表示功能键F1等。 同击键消息一样,MFC中的ClassWizard也提供相应的字符消息处理框架,并自动添加了当前类的WM_CHAR消息处理函数调用,它具有下列函数原型: afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags ); 参数nChar表示键的ASCII码,nRepCnt表示当用户按住一个键时的重复计数,nFlags表示字符消息标志。
74
3.3常用控件 ● 代码中,第1个if语句是用来判断是否按下了F7功能,第2个if语句用来判断用户是否单击对话框的[确定]按钮。一旦DoModal返回的值为IDOK,即按下了对话框的[确定]按钮,对话框中的数据才是有效的。 ● 代码中的Format是CString类的一个经常使用的成员函数,它通过格式操作使任意类型的数据转换成一个字符串。该函数的第一个参数是带格式的字符串,其中的“%s”就是一个格式符,每一个格式符依次对应于该函数的后面参数表中的参数项。例如格式字符串中第一个%s对应于dlg.m_strName。CString类的Format和C语言库函数printf十分相似。 (2)在文件Ex_InputView.cpp的前面添加CInputDlg类的头文件包含: #include "Ex_InputDoc.h" #include "Ex_InputView.h" #include "InputDlg.h" (3)编译并运行,按F7键弹出如前图3.35所示的对话框。单击成绩1的旋转按钮控件将以0.5增量来改变它的伙伴窗口的数值。而成绩2和成绩3的旋转按钮控件由于设置了自动结伴整数(Set buddy integer)属性,因此它按默认增量1自动改变伙伴窗口的数值。
75
3.3常用控件 列表框 列表框( )是一个列有许多项目让用户选择的控件。它与单选按钮组或复选框组一样,都可让用户在其中选择一个或多个项,但不同的是,列表框中项的数目是可灵活变化的,程序运行时可往列表框中添加或删除某些项。并且,当列表框中项的数目较多,而不能一次全部显示时,还可以自动提供滚动条来让用户浏览其余的列表项。 1. 列表框的样式和消息 按性质来分,列表框有单选、多选、扩展多选以及非选四种类型,如图3.38所示。 图3.38 不同类型的列表框
76
3.3常用控件 表3.9 列表框的样式(Style)属性 项 目 说 明 选择(Selection)
项 目 说 明 选择(Selection) 指定列表框的类型:单选(Single)、多选(Multiple)、扩展多选(Extended)、不选(None)。 所有者绘制(Owner draw) 自画列表框,默认为否(No) 有字串(Has strings) 选中时,在自画列表框中的项目中含有字符串文本 边框(Border) 选中时,使列表框含有边框 分类(Sort) 选中时,列表框的项目按字母顺序排列 通知(Notify) 选中时,当用户对列表框操作,就会向父窗口发送通知消息 多列(Multi-column) 选中时,指定一个具有水平滚动的多列列表框 水平滚动(Horizontal scroll) 选中时,在列表框中创建一个水平滚动条 垂直滚动(Vertical scroll) 选中时,在列表框中创建一个垂直滚动条 不刷新屏幕(No redraw) 选中时,列表框发生变化后不会自动重画。 使用制表站(Use tabstops) 选中时,允许使用停止位来调整列表项的水平位置 需要键输入(Want key input) 选中此项,当用户按键且列表框有输入焦点时,就会向列表框的父窗口发送相应消息。 禁止不滚动(Disable no scroll) 选中时,即使列表框的列表项能全部显示,垂直滚动条也会显示,但此时是禁用的(灰显)。 没有完整高度(No integral height) 选中时,在创建列表框的过程中,系统会把用户指定的尺寸完全作为列表框的尺寸,而不管是否会有项目在列表框不能完全显示出来
77
3.3常用控件 当列表框中发生了某个动作,如用户双击选择了列表框中某一项时,列表框就会向其父窗口发送一条通知消息。常用的通知消息如表3.10所示。 表3.10 列表框的通知消息 通知消息 说 明 LBN_DBLCLK 用户双击列表框的某项字符串时发送此消息 LBN_KILLFOCUS 列表框失去键盘输入焦点时时发送此消息 LBN_SELCANCEL 当前选择项被取消时发送此消息 LBN_SELCHANGE 列表框中的当前选择项将要改变时发送此消息 LBN_SETFOCUS 列表框获得键盘输入焦点时发送此消息
78
3.3常用控件 2. 列表框的基本操作 当列表框创建之后,往往要添加、删除、改变或获取列表框中的列表项,这些操作都可以调用MFC的CListBox类成员函数来实现。需要注意的是:列表框的项除了用字符串来标识外,还常常通过索引来确定。索引表明项目在列表框中排列的位置,它是以0为基数的,即列表框中第一项的索引是0,第二项的索引是1,依次类推。 (1) 添加列表项。列表框创建时是一个空的列表,需要用户添加或插入一些列表项。CListBox类成员函数AddString 和InsertString 分别用来添加列表项,其函数原型如下: int AddString( LPCTSTR lpszItem ); int InsertString( int nIndex, LPCTSTR lpszItem ); 上述两个函数只能将字符串增加到列表框中,但有时用户还会需要根据列表项使用其他数据。这时,就需要调用CListBox的SetItemData和SetItemDataPtr,它们能使用户数据和某个列表项关联起来。 int SetItemData( int nIndex, DWORD dwItemData ); int SetItemDataPtr( int nIndex, void* pData ); 其中,SetItemData是将一个32位数与某列表项(由nIndex指定)关联起来,而SetItemDataPtr可以将用户的数组、结构体等大量的数据与列表项关联。若有错误产生时,两个函数都将返回LB_ERR。
79
3.3常用控件 (2) 删除列表项。CListBox类成员函数DeleteString和ResetContent分别用来删除指定的列表项和清除列表框所有项目。它们的函数原型如下: int DeleteString( UINT nIndex ); // nIndex指定要删除的列表项的索引 void ResetContent( ); 需要注意的是,若在添加列表项时使用SetItemDataPtr函数,不要忘记在进行删除操作时及时将关联数据所占的内存空间释放出来。 (3) 查找列表项。为了保证列表项不会重复地添加在列表框中,有时还需要对列表项进行查找。CListBox类成员函数FindString 和FindStringExact分别用来在列表框中查找所匹配的列表项。其中,FindStringExact的查找精度最高。 int FindString( int nStartAfter, LPCTSTR lpszItem ) const; int FindStringExact( int nIndexStart, LPCTSTR lpszFind ) const; 其中,lpszFind和lpszItem指定要查找的列表项文本,nStartAfter和nIndexStart指定查找的开始位置,若为-1,则从头至尾查找。查到后,这两个函数都将返回所匹配列表项的索引,否则返回LB_ERR。
80
3.3常用控件 (4) 列表框的单项选择。当选中列表框中某个列表项,用户可以使用CListBox::GetCurSel来获取这个结果,与该函数相对应的CListBox::SetCurSel 函数是用来设定某个列表项呈选中状态(高亮显示)。 int GetCurSel( ) const; // 返回当前选择项的索引 int SetCurSel( int nSelect ); 其中,nSelect指定要设置的列表项索引,错误时这两个函数都将返回LB_ERR。 若要获取某个列表项的字符串,可使用下列函数: int GetText( int nIndex, LPTSTR lpszBuffer ) const; void GetText( int nIndex, CString& rString ) const; 其中,nIndex指定列表项索引,lpszBuffer 和rString是用来存放列表项文本。 (5) 列表框的多项选择。当在列表框的样式(Style)属性对话框中选中多选(Multiple)或扩展多选(Extended)类型后,就可以在列表框中进行多项选择。要想获得选中的多个选项,需要用MFC ClassWizard映射列表框控件的LBN_SELCHANGE消息,并添加类似下面的一些代码:
81
3.3常用控件 void CListBoxDlg::OnSelchangeList1() {
int nCount = m_list.GetSelCount(); // 获取用户选中的项数 if (nCount == LB_ERR) return; int *buffer = new int[nCount]; // 开辟缓冲区 m_list.GetSelItems(nCount,buffer); // 将各个选项的索引号内容存放在缓冲区中 CString allStr = NULL, str; for (int i=0; i<nCount; i++) m_list.GetText(buffer[i], str); // 获得各个索引的项目文本 allStr = allStr + "[" + str + "]"; // 处理项目文本 } delete []buffer; // 释放内存 // MessageBox(allStr); // 处理获得的文本
82
3.3常用控件 3. 示例:城市邮政编码 在一组城市邮政编码中,城市名和邮政编码是一一对应的。为了能添加和删除城市邮政编码列表项,需要设计一个这样的对话框,如图3.40所示。单击[添加]按钮,将城市名和邮政编码添加到列表框中,为了使添加不重复,还要进行一些判断操作。单击列表框的城市名,将在编辑框中显示出城市名和邮政编码。单击[删除]按钮,删除当前的列表项。 图3.40 城市邮政编码 实现本例有两个要点:一是在添加时需要通过FindString 或FindStringExact来判断添加的列表项是否重复,然后通过SetItemData将邮政编码(将它视为一个32位整数)与列表项关联起来;二是由于删除操作是针对当前选中的列表项的,如果当前没有选中的列表项则应通过EnableWindow(FLASE)使[删除]按钮灰显,即不能单击它。本例分两个部分来阐述:创建并设计对话框、完善代码。
83
3.3常用控件 [例Ex_City] 城市邮政编码对话框 1) 创建并设计对话框
(1) 用MFC AppWizard(exe)创建一个默认的对话框应用程序Ex_City。 (2) 将对话框的标题设为“城市邮政编码”。删除“TODO: 在这里设置对话控制。”静态文本控件和 [取消]按钮,将[确定]按钮标题改为“退出”。 (3) 打开对话框网格,参看图3.40的控件布局,用编辑器为对话框添加如表3.11所示的一些控件。 表3.11 城市邮政编码对话框添加的控件 添加的控件 ID标识符 标 题 其他属性 列表框 IDC_LIST1 —— 默认 编辑框(城市名) IDC_EDIT_CITY 编辑框(邮政编码) IDC_EDIT_ZIP 按钮(添加) IDC_BUTTON_ADD 添加 按钮(修改) IDC_BUTTON_DEL 修改
84
3.3常用控件 (4) 按Ctrl+W键,打开MFC ClassWizard对话框,并切换至Member Variables页面,看看Class name是否是CEx_CityDlg,然后选中所需的控件ID标识符,双击鼠标或单击Add Variables按钮,依次为下列控件增加成员变量。如表3.12所示。 表3.12 控件变量 控件ID标识符 变量类别 变量类型 变量名 范围和大小 IDC_LIST1 Control CListBox m_ListBox —— IDC_EDIT_CITY Value CString m_strCity 40 IDC_EDIT_ZIP DWORD m_dwZipCode 100000~999999
85
3.3常用控件 2) 完善代码 (1) 将项目工作区切换到ClassView页面,展开结点,右击CEx_CityDlg类名,从弹出的快捷菜单中选择“Add Member Function”,弹出如图3.41所示的对话框,在“函数类型”(Function Type)框中输入BOOL,在“函数声明”(Function Declaration)框中输入IsValidate,单击[确定]按钮。 图3.41 添加成员函数
86
3.3常用控件 (2)在CEx_CityDlg::IsValidate函数输入下列代码:
BOOL CEx_CityDlg::IsValidate() { UpdateData(); m_strCity.TrimLeft(); if (m_strCity.IsEmpty()) MessageBox("城市名输入无效!"); return FALSE; } return TRUE; IsValidate函数的功能是判断城市名编辑框中的内容是否是有效的字符串。代码中,TrimLeft是CString类的一个成员函数,用来去除字符串左边的白字符。
87
3.3常用控件 (3)在CEx_CityDlg::OnInitDialog函数添加下列代码:
BOOL CEx_CityDlg::OnInitDialog() { CDialog::OnInitDialog(); … m_dwZipCode = ; // 设置初始的邮政编码 UpdateData( FALSE ); // 将邮政编码显示在控件中 GetDlgItem(IDC_BUTTON_DEL)->EnableWindow( FALSE ); // 使[删除]按钮灰显 return TRUE; // return TRUE unless you set the focus to a control }
88
3.3常用控件 (4)打开MFC ClassWizard,切换到Messsage Maps页面,为按钮IDC_BUTTON_ADD添加BN_CLICKED的消息映射,并增加下列代码: void CEx_CityDlg::OnButtonAdd() { if (!IsValidate()) return; int nIndex = m_ListBox.FindStringExact( -1, m_strCity ); if (nIndex != LB_ERR ) MessageBox("该城市已添加!"); return; } nIndex = m_ListBox.AddString( m_strCity ); m_ListBox.SetItemData( nIndex, m_dwZipCode );
89
3.3常用控件 (5)用MFC ClassWizard为按钮IDC_BUTTON_DEL添加BN_CLICKED的消息映射,并增加下列代码:
void CEx_CityDlg::OnButtonDel() { int nIndex = m_ListBox.GetCurSel(); if (nIndex != LB_ERR ) m_ListBox.DeleteString( nIndex ); else GetDlgItem(IDC_BUTTON_DEL)->EnableWindow( FALSE ); } (6)用MFC ClassWizard为列表框IDC_LIST1添加LBN_SELCHANGE(当前选择项发生改变)的消息映射,并增加下列代码。这样,当单击列表框的城市名时,将会在编辑框中显示出城市名和邮政编码。
90
3.3常用控件 (7)编译运行后,按前图3.40中的内容进行测试。 void CEx_CityDlg::OnSelchangeList1()
{ int nIndex = m_ListBox.GetCurSel(); if (nIndex != LB_ERR ) m_ListBox.GetText( nIndex, m_strCity ); m_dwZipCode = m_ListBox.GetItemData( nIndex ); UpdateData( FALSE ); // 使用当前列表项所关联的内容显示在控件上 GetDlgItem(IDC_BUTTON_DEL)->EnableWindow( TRUE ); } (7)编译运行后,按前图3.40中的内容进行测试。
91
3.3常用控件 组合框 1. 组合框的样式类型 按照组合框的主要样式特征,可把组合框分为3类:简单组合框、下拉式组合框、下拉式列表框。如图3.42所示。 简单组合框和下拉式组合框都包含有列表框和编辑框,但是简单组合框中的列表框不需要下拉,是直接显示出来的,而当用户单击下拉式组合框中的下拉按钮时,下拉的列表框才被显示出来。下拉式列表框虽然具有下拉式的列表,却没有文字编辑功能。 组合框还有其他一些样式,这些样式可在如图3.43所示的组合框的属性对话框中设置。除了“分类(Sort,排序)”选项外,其它选项的含义与列表框或编辑框的样式属性相同。 图3.42 组合框的类型 图3.43 组合框的属性对话框
92
3.3常用控件 2. 组合框的通知消息 在组合框的通知消息中,有的是列表框发出的,有的是编辑框发出的,如表3.13所示。
2. 组合框的通知消息 在组合框的通知消息中,有的是列表框发出的,有的是编辑框发出的,如表3.13所示。 3. 组合框常见操作 组合框的操作大致分为两类,一类是对组合框中的列表框进行操作,另一类是对组合框中的编辑框进行操作。这些操作都可以调用CComboBox类的成员函数来实现,见表3.14。 表3.13 组合框的通知消息 通知消息 说 明 CBN_CLOSEUP 当组合框的列表关闭时发送此消息 CBN_DBLCLK 用户双击组合框的某项字符串时发送此消息 CBN_DROPDOWN 当组合框的列表打开时发送此消息 CBN_EDITCHANGE 同编辑框的EN_CHANGE消息 CBN_EDITUPDATE 同编辑框的EN_UPDATE消息 CBN_SELENDCANCEL 当前选择项被取消时发送此消息 CBN_SELENDOK 当用户选择一个项并按下ENTER键或单击下拉箭头 ()隐藏列表框时发送此消息。 CBN_KILLFOCUS 组合框失去键盘输入焦点时时发送此消息 CBN_SELCHANGE 组合框中的当前选择项将要改变时发送此消息 CBN_SETFOCUS 组合框获得键盘输入焦点时发送此消息
93
3.3常用控件 (1)上述有返回值的成员函数中,若不特别说明,当函数产生错误时其返回值一般都CB_ERR。
(2)由于组合框的一些编辑操作与编辑框CEdit的成员函数相似,如:GetEditSet、SetEditSel等,因此这些成员函数没有在上述表中列出。 4. 示例:城市邮政编码和区号 前面的示例中,只是简单的涉及到城市名和邮政编码的对应关系。实际上,城市名还和区号一一对应,为此本例需要设计这样的对话框,如图3.44所示。 单击[添加]按钮将城市名、邮政编码和区号添加到组合框中,在添加前同样需要进行重复性的判断。选择组合框中的城市名,将在编辑框中显示出邮政编码和区号,单击[修改]按钮,将以城市名作为组合框的查找关键字,找到后修改其邮政编码和区号内容。 图3.44 城市邮政编码和区号
94
3.3常用控件 [例Ex_CityZone] 城市邮政编码和区号对话框 1)创建并设计对话框
(1)用MFC AppWizard(exe)创建一个默认的对话框应用程序Ex_CityZone。 (2)将对话框的标题设为“城市邮政编码和区号”。删除“TODO: 在这里设置对话控制。”静态文本控件和[取消]按钮,将[确定]按钮标题改为“退出”。 (3)打开对话框网格,参看图3.44的控件布局,用编辑器为对话框添加如表3.15所示的 表3.15 城市邮政编码和区号对话框添加的控件 添加的控件 ID标识符 标 题 其他属性 组合框 IDC_COMBO1 —— 默认 编辑框(邮政编码) IDC_EDIT_ZIP 编辑框(区号) IDC_EDIT_ZONE 按钮(添加) IDC_BUTTON_ADD 添加 按钮(修改) IDC_BUTTON_CHANGE 修改
95
3.3常用控件 需要说明的是,在组合框添加到对话框模板后,一定要单击组合框的下拉按钮( ),然后调整出现的下拉框大小,如图3.45所示,否则组合框可能因为下拉框太小而无法显示其下拉列表项。 下拉框 图3.45 调整组合框的下拉框 表3.16 控件变量 控件ID标识符 变量类别 变量类型 变量名 范围和大小 IDC_COMBO1 Control CComboBox m_ComboBox —— Value CString m_strCity 20 IDC_EDIT_ZONE m_strZone 10 IDC_EDIT_ZIP m_strZip 6
96
3.3常用控件 (4)打开MFC ClassWizard的Member Variables页面,看看Class name是否是CEx_CityZoneDlg,然后选中所需的控件ID标识符,双击鼠标或单击Add Variables按钮。依次为下列控件增加成员变量。如表3.16所示。 2)完善代码 (1)将项目工作区切换到ClassView页面,展开结点,右击CEx_CityZoneDlg类名,从弹出的快捷菜单中选择“Add Member Function”,弹出Add Member Function对话框,在函数类型框中输入BOOL,在函数声明框中输入IsValidate,单击[确定]按钮。 (2)在CEx_CityZoneDlg::IsValidate函数输入下列代码:
97
3.3常用控件 BOOL CEx_CityZoneDlg::IsValidate() {
UpdateData(); m_strCity.TrimLeft(); if (m_strCity.IsEmpty()) MessageBox("城市名输入无效!"); return FALSE; } m_strZip.TrimLeft(); if (m_strZip.IsEmpty()) MessageBox("邮政编码输入无效!"); return FALSE; m_strZone.TrimLeft(); if (m_strZone.IsEmpty()) MessageBox("区号输入无效!"); return FALSE; return TRUE;
98
3.3常用控件 (3)打开MFC ClassWizard,切换到Messsage Maps页面,为按钮IDC_BUTTON_ADD添加BN_CLICKED的消息映射,并增加下列代码: void CEx_CityZoneDlg::OnButtonAdd() { if (!IsValidate()) return; int nIndex = m_ComboBox.FindStringExact( -1, m_strCity ); if (nIndex != CB_ERR ) MessageBox("该城市已添加!"); return; } CString strData; strData.Format("%s,%s", m_strZip, m_strZone); // 将邮政编码和区号合并为一个字符串 m_ComboBox.SetItemDataPtr( nIndex, new CString(strData) );
99
3.3常用控件 (4)用MFC ClassWizard为按钮IDC_BUTTON_CHANGE添加BN_CLICKED的消息映射,并增加下列代码: void CEx_CityZoneDlg::OnButtonChange() { if (!IsValidate()) return; int nIndex = m_ComboBox.FindStringExact( -1, m_strCity ); if (nIndex != CB_ERR ) delete (CString*)m_ComboBox.GetItemDataPtr( nIndex ); CString strData; strData.Format("%s,%s", m_strZip, m_strZone); m_ComboBox.SetItemDataPtr( nIndex, new CString(strData) ); }
100
3.3常用控件 (5)用MFC ClassWizard为组合框IDC_COMBO1添加CBN_SELCHANGE(当前选择项发生改变)的消息映射,并增加下列代码: void CEx_CityZoneDlg::OnSelchangeCombo1() { int nIndex = m_ComboBox.GetCurSel(); if (nIndex != CB_ERR ) m_ComboBox.GetLBText( nIndex, m_strCity ); CString strData = *(CString*)m_ComboBox.GetItemDataPtr( nIndex ); // 分解字符串 int n = strData.Find(','); m_strZip = strData.Left( n ); // 前面的n个字符 m_strZone = strData.Mid( n+1 );// 从中间第n+1字符到未尾的字符串 UpdateData( FALSE ); }
101
3.3常用控件 (6)用MFC ClassWizard为对话框添加WM_DESTROY的消息映射,并增加下列代码:
void CEx_CityZoneDlg::OnDestroy() // 此消息是当对话框关闭时发送的 { for (int nIndex = m_ComboBox.GetCount()-1; nIndex>=0; nIndex--) // 删除所有与列表项相关联的CString数据,并释放内存 delete (CString *)m_ComboBox.GetItemDataPtr(nIndex); } CDialog::OnDestroy(); 需要说明的是,当对话框从屏幕消失后,对话框被清除时发送WM_DESTROY消息。在此消息的映射函数中添加一些对象删除代码,以便在对话框清除前有效地释放内存空间。 (7)编译运行并测试。
102
3.3常用控件 进展条和日期时间控件 进展条通常用来说明一个操作的进度,并在操作完成时从左到右填充进展条,这个过程可以让用户看到任务还有多少要完成。而日期时间控件可以允许用户选择日期和时间。 1. 进展条 进展条( )是一个如图3.46所示的控件。除了能表示一个过程进展情况外,使用进展条还可表明温度、水平面或类似的测量值。 图3.46 进展条 (1) 进展条的样式。打开进展条的属性对话框,可以看到它的“样式”属性并不是很多。其中,“边框”(Border)用来指定进展条是否有边框,“垂直”(Vertical)用来指定进展是水平还是垂直的,若选中,则为垂直的。“平滑”(Smooth)表示平滑地填充进展条,若不选中则表示将用块来填充,就像图3.46那样。
103
3.3常用控件 (2)进展条的基本操作。进展条的基本操作有:设置其范围、当前位置、设置增量等。这些操作都是通过CProgressCtrl类的相关成员函数来实现的。 int SetPos( int nPos ); int GetPos(); 这两个函数分别用来设置和获取进展条的当前位置。需要说明的是,这个当前位置是指在SetRange中的上限和下限范围之间的位置。 void SetRange( short nLower, short nUpper ); void SetRange32(int nLower, int nUpper ); void GetRange( int & nLower, int& nUpper ); 它们分别用来设置和获取进展条范围的上限和下限值。一旦设置后,还会重画此进展条来反映新的范围。成员函数SetRange32为进展条设置32位的范围。参数nLower和nUpper 分别表示范围的下限(默认值为0)和上限(默认值为100)。 int SetStep( int nStep ); 该函数用来设置进展条的步长并返回原来的步长,默认步长为10。 int StepIt(); 该函数将当前位置向前移动一个步长并重画进展条以反映新的位置。函数返回进展条上一次的位置。
104
3.3常用控件 2. 日期时间控件 日期时间控件( )是一个组合控件,它由编辑框和一个下拉按钮组成,单击控件的右边的下拉按钮,即可弹出月历控件可供用户选择日期,如图3.47所示。 图3.47 日期时间控件
105
3.3常用控件 日期时间有许多样式,这些样式用来定义日期时间控件的外观及操作方式,它们可以在日期时间控件属性对话框中进行设置,如图3.48所示。表3.17列出其各项含义。 图3.48 日期时间控件属性对话框 表3.17 日期时间控件的样式(Style)属性 项 目 说 明 格式(Format) 日期时间控件的格式有:短日期(Short Date)、长日期(Long Date)、时间(Time)。 靠右排列(Right Align) 下拉月历右对齐控件。 使用旋转控件(Use Spin Control) 选中此项,在控件的右边出现一个旋转按钮用来调整日期。否则,控件的右边是一个下拉按钮用来弹出月历。 显示没有(Show None) 选中此项,日期前面显示一个复选框,当用户选中复选框时,方可键入或选择一个日期。 允许编辑(Allow Edit) 选中此项,日期和时间允许在编辑框中直接更改。
106
3.3常用控件 一般来说,用户最关心地是如何设置和获取日期时间控件的日期或时间。CDateTimeCtrl类的成员函数SetTime和GetTime可以满足这样的要求,它们最常用的函数原型如下: BOOL SetTime( const CTime* pTimeNew ); DWORD GetTime( CTime& timeDest ) const; 其中,CTime是Visual C++用于时间操作的类。 3. 示例:自动时间显示 在MFC应用程序中,自动运行的方法是通过计时器来实现的。Windows的计时器能周期性地按一定的时间间隔向应用程序发送WM_TIMER消息。由于它能实现“实时更新”以及“后台运行”等功能,因而在应用程序中计时器是一个难得的程序方法。 应用程序是通过CWnd的SetTimer函数来设置并启动计时器的,这个函数的原型如下: UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );
107
3.3常用控件 SetTimer函数成功调用后返回新计时器的标识值。当应用程序不再使用计时器时,可调用CWnd:: KillTimer函数来停止WM_TIMER消息的传送,其函数原型如下: BOOL KillTimer( int nIDEvent ); 其中nIDEvent和用户调用SetTimer函数设置的计时器标识值是一致的。 对于WM_TIMER消息,MFC ClassWizard会将其映射成具有下列原型的消息处理函数: afx_msg void OnTimer( UINT nIDEvent ); 通过nIDEvent可判断出WM_TIMER是哪个计时器传送的。 在本例中,对话框中的日期时间控件能自动显示当前系统中的时间,且还通过进展条在线地显示0~59秒的情况,如图3.49所示。 图3.49 自动时间显示
108
3.3常用控件 [例Ex_Timer] 自动时间显示
(1)用MFC AppWizard(exe)创建一个默认的对话框应用程序Ex_Timer。 将对话框的标题设为“自动时间显示”。删除“TODO: 在这里设置对话控制。”静态文本控件和[取消]按钮,将[确定]按钮标题改为“退出”。 (2)打开对话框网格,参看图3.49的控件布局,向对话框添加2个静态标签控件、1个日期时间控件(设为“时间”格式,其它默认)、1个进展条控件(去除“边框”选项,选中“平滑”项,其它默认)。 (3)打开MFC ClassWizard的Member Variables页面,为进展条控件添加Control类型变量m_wndProgress,为日期时间控件添加Value类型(CTime)变量m_curTime。 (5)再次打开MFC ClassWizard,切换到Messsage Maps页面,为CEx_TimerDlg类添加WM_TIMER消息映射,并增加下列代码: void CEx_TimerDlg::OnTimer(UINT nIDEvent) { m_curTime = CTime::GetCurrentTime(); // 获取当前时间 UpdateData( FALSE ); // 结果显示在控件中 int nSec= m_curTime.GetSecond(); // 获取当前时间的秒数 m_wndProgress.SetPos( nSec ); // 设定进展条的当前位置 CDialog::OnTimer(nIDEvent); }
109
3.3常用控件 (6)在CEx_TimerDlg::OnInitDialog中添加下列代码:
BOOL CEx_TimerDlg::OnInitDialog() { CDialog::OnInitDialog(); … m_wndProgress.SetRange( 0, 59 ); SetTimer( 1, 200, NULL ); return TRUE; // return TRUE unless you set the focus to a control } (7)编译运行。 需要说明的是,由于OnTimer 函数中是通过获取系统时间来显示相应的内容,因此SetTimer中所指定的消息发生的时间间隔对结果基本没有影响,因此间隔设置小一些,可以让结果显示更加可靠一些。
110
3.3常用控件 3.3.6 滚动条和滑动条 滚动条和滑动条可以完成诸如定位、指示等之类的操作。滚动箭头按钮 滚动条 滚动块 1. 滚动条
滚动条和滑动条 滚动条和滑动条可以完成诸如定位、指示等之类的操作。滚动箭头按钮 滚动条 滚动块 1. 滚动条 滚动条是一个独立的窗口,虽然它具有直接的输入焦点,但却不能自动地滚动窗口内容,因此,它的使用受到一定的限制。 根据滚动条的走向,可分为垂直滚动条( )和水平滚动条( )两种类型。这两种类型滚动条的组成部 图3.50 滚动条外观 滚动箭头按钮 滚动条 滚动块 分都是一样的,两端都有两个箭头按钮,中间有一个可沿滚动条方向移动的滚动块。如图3.50所示。 (1)滚动条的基本操作。滚动条的基本操作一般包括设置和获取滚动条的范围及滚动块的相应位置。
111
3.3常用控件 由于滚动条控件的默认滚动范围是0到0,因此在使用滚动条之前必须设定其滚动范围。在MFC的CScrollBar类中,函数SetScrollRange是用来设置滚动条的滚动范围,其原型为: SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw = TRUE ); 其中,nMinPos和nMaxPos表示滚动位置的最小值和最大值。bRedraw为重画标志,当为TRUE时,滚动条被重画。 在CScrollBar类中,设置滚动块位置操作是由SetScrollPos函数来完成的,其原型如下: int SetScrollPos( int nPos, BOOL bRedraw = TRUE ); 其中,nPos表示滚动块的新位置,它必须是在滚动范围之内。 与SetScrollRange 和SetScrollPos相对应的两个函数是分别用来获取滚动条的当前范围以及当前滚动位置: void GetScrollRange( LPINT lpMinPos, LPINT lpMaxPos ) ; int GetScrollPos(); 其中,LPINT是整型指针类型,lpMinPos和lpMaxPos分别用来返回滚动块最小和最大滚动位置。
112
3.3常用控件 需要说明的是:在CScrollBar类的成员函数中,还可以用SetScrollInfo和GetScrollInfo来代替上面提到的四个函数。与前面的函数相比,使用SetScrollInfo函数还能使滚动块的大小自动参数大小改变而改变,并且这两个函数都使用下面的SCROLLINFO结构: typedef struct tagSCROLLINFO { // si UINT cbSize; // SCROLLINFO结构本身的字节大小 UINT fMask; // 见下 int nMin; // 最小滚动位置 int nMax; // 最大滚动位置 UINT nPage; // 页面尺寸 int nPos; // 滚动块的位置。 int nTrackPos; // 表示滚动块当前被拖动的位置 } SCROLLINFO;
113
3.3常用控件 (2)WM_HSCROLL或WM_VSCROLL消息。当用户对滚动条进行操作时,滚动条就会向父窗口发送WM_HSCROLL或WM_VSCROLL消息(分别对应于水平滚动条和垂直滚动条)。这些消息是通过MFC ClassWizard在其对话框(滚动条的父窗口)中进行映射的,并产生相应的消息映射函数OnHScroll和OnVScroll,这两个函数具有下列原型: afx_msg void OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ); afx_msg void OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ); 其中,nPos表示滚动块的当前位置,pScrollBar表示由滚动条控件的指针, nSBCode表示滚动条的通知消息。图3.51表示当鼠标单击滚动条的不同部位时,所产生的不同通知消息。
114
3.3常用控件 图3.51 滚动条通知代码与位置的关系 SB_LINEUP SB_PAGEUP SB_THUMBTRACK和
SB_LINELEFT SB_LINERIGHT SB_PAGELEFT SB_PAGERIGHT 图3.51 滚动条通知代码与位置的关系 SB_LINEUP SB_PAGEUP SB_THUMBTRACK和 SB_THUMBPOSITION SB_PAGEDOWN SB_LINEDOWN
115
3.3常用控件 表3.18列出了各通知消息的含义。 表3.18 滚动条的通知消息 通知消息 说 明 SB_LEFT、SB_RIGHT
表3.18 滚动条的通知消息 通知消息 说 明 SB_LEFT、SB_RIGHT 滚动到最左端或最右端时发送此消息 SB_TOP 、SB_BOTTOM 滚动到最上端或最下端时发送此消息 SB_LINELEFT、SB_LINERIGHT 向左或右滚动一行(或一个单位)时发送此消息 SB_LINEUP、SB_LINEDOWN 向上或下滚动一行(或一个单位)时发送此消息 SB_PAGELEFT、SB_PAGERIGHT 向左或右滚动一页时发送此消息 SB_PAGEUP、SB_PAGEDOWN 向上或下滚动一页时发送此消息 SB_THUMBPOSITION 滚动到某绝对位置时发送此消息 SB_THUMBTRACK 拖动滚动块时发送此消息 SB_ENDSCROLL 结束滚动
116
3.3常用控件 2. 滑动条 滑动条控件( )是由滑动块和可选的刻度线组成的,如图3.52所示。当用户用鼠标或方向键移动滑动块时,该控件发送通知消息来表明这些改变。 图3.52 带刻度线的滑动条 滑动块 刻度线 (1)滑动条的风格和消息。滑动条控件有许多样式,它们都可以通过滑动条控件的属性对话框进行设置,如图3.53所示。表3.19列出该属性对话框的各项含义。 图3.53 滑动条属性对话框
117
3.3常用控件 表3.19 滑动条控件的Style属性 项 目 说 明 方向(Orientation)
项 目 说 明 方向(Orientation) 控件放置方向:垂直(Vertical)、水平(Horizontal,默认)。 点(Point) 刻度线在滑动条控件中放置的位置:两者(Both,两边都有)、顶部/左侧(Top/Left,水平滑动条的上边或垂直滑动条的左边,同时滑动块的尖头指向有刻度线的哪一边)、底部/右侧(Bottom/Right,水平滑动条的下边或垂直滑动条的右边,同时滑动块的尖头指向有刻度线的哪一边) 打勾标记(Tick marks) 选中此项,在滑动条控件上显示刻度线 自动打勾(Auto ticks) 选中此项,滑动条控件上的每个增量位置处都有刻度线,并且增量大小自动根据其范围来确定 边框(Border) 选中此项,控件周围有边框 允许选择(Enable selection) 选中此项,控件中供用户选择的数值范围高亮显示
118
3.3常用控件 滑动条的通知消息代码常见的有:TB_BOTTOM 、TB_ENDTRACK、TB_LINEDOWN 、TB_LINEUP、TB_PAGEDOWN 、TB_PAGEUP 、TB_THUMBPOSITION 、TB_TOP和TB_THUMBTRACK等。这些消息代码都来自于WM_HSCROLL或WM_VSCROLL消息,其具体含义同滚动条。 (2) 滑动条的基本操作。MFC的CSliderCtrl 类提供了滑动条控件的各种操作函数,这其中包括范围、位置设置和获取等。 成员函数SetPos和SetRange分别用来设置滑动条的位置和范围,其原型如下: void SetPos( int nPos ); void SetRange( int nMin, int nMax, BOOL bRedraw = FALSE ); 其中,参数nPos表示新的滑动条位置。bMin和nMax表示滑动条的最小和最大位置,bRedraw表示重画标志,为TRUE时,滑动条被重画。与这两个函数相对应的成员函数GetPos和GetRange是分别用来获取滑动条的位置和范围的。
119
3.3常用控件 成员函数SetTic用来设置滑动条控件中的一个刻度线的位置。函数成功调用后返回非零值;否则返回0。函数原型如下:
BOOL SetTic( int nTic ); 其中,参数nTic表示刻度线的位置。 成员函数SetTicFreq用来设置显示在滑动条中的刻度线的疏密程度。其函数原型如下: void SetTicFreq( int nFreq ); 其中,参数nFreq表示刻度线的疏密程度。例如,如果参数被设置为2,则在滑动条的范围中每两个增量显示一个刻度线。要使这个函数有效,必须在属性对话框中选中Auto ticks项。 成员函数ClearTics用来从滑动条控件中删除当前的刻度线。其函数原型如下: void ClearTics( BOOL bRedraw = FALSE ); 其中,参数bRedraw表示重画标志。若该参数为TRUE,则在选择被清除后重画滑动条。 成员函数SetSelection用来设置一个滑动条控件中当前选择的开始和结束位置。其函数原型如下: void SetSelection( int nMin, int nMax ); 其中,参数nMin、nMax表示滑动条的开始和结束位置。
120
3.3常用控件 3. 示例:调整对话框背景颜色 设置对话框背景颜色有许多方法,这里采用最简单的也是最直接的方法,即通过映射 WM_CTLCOLOR(当子窗口将要绘制时发送的消息,以便能使用指定的颜色绘制控件)来达到改变背景颜色的目的。本例通过滚动条和两个滑动条来调整Visual C++所使用的RGB颜色的三个分量:R(红色)、G(绿色)和B(蓝色),如图3.54所示。(示例过程分创建并设计对话框、完善代码两部分来阐述) 图3.54 调整对话框背景颜色 [例Ex_BkColor] 调整对话框背景颜色 1)创建并设计对话框 (1)用MFC AppWizard(exe)创建一个默认的对话框应用程序Ex_BkColor。 (2)将对话框的标题设为“调整对话框背景颜色”。删除“TODO: 在这里设置对话控制。”静态文本控件和[取消]按钮,将[确定]按钮标题改为“退出”。
121
3.3常用控件 (3)打开对话框网格,参看图3.54的控件布局,为对话框添加如表3.20所示的一些控件。
表3.20 对话框添加的控件 添加的控件 ID标识符 标 题 其他属性 水平滚动条(红色) IDC_SCROLLBAR_RED —— 默认 滑动条(绿色) IDC_SLIDER_GREEN 滑动条(蓝色) IDC_SLIDER_BLUE (4)打开ClassWizard的Member Variables页面,选中所需的控件ID标识符,双击鼠标。依次为下列控件增加成员变量。如表3.21所示。 表3.21 控件变量 控件ID标识符 变量类别 变量类型 变量名 范围和大小 IDC_SCROLLBAR_RED Control CScrollBar m_scrollRed —— IDC_SLIDER_GREEN CSliderCtrl m_sliderGreen —— Value int m_nGreen IDC_SLIDER_BLUE m_sliderBlue m_nBlue
122
3.3常用控件 (5)为CEx_BkColorDlg类添加两个成员变量,一个是int型m_nRedValue,用来指定RGB中的红色分量,另一个是画刷CBrush类对象m_Brush,用来设置对话框背景所需要的画刷。 2)完善代码 (1)在CEx_BkColorDlg::OnInitDialog中添加下列初始化代码: BOOL CEx_BkColorDlg::OnInitDialog() { CDialog::OnInitDialog(); … // TODO: Add extra initialization here m_scrollRed.SetScrollRange(0, 255); m_sliderBlue.SetRange(0, 255); m_sliderGreen.SetRange(0, 255); m_nBlue = m_nGreen = m_nRedValue = 192; UpdateData( FALSE ); m_scrollRed.SetScrollPos(m_nRedValue); return TRUE; // return TRUE unless you set the focus to a control }
123
3.3常用控件 (2)用MFC ClassWizard为CEx_BkColorDlg类添加WM_HSCROLL消息映射,并添加下列代码:
(3)用MFC ClassWizard为CEx_BkColorDlg类添加WM_CTLCOLOR消息映射,并添加下列代码: HBRUSH CEx_BkColorDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { UpdateData(TRUE); COLORREF color = RGB(m_nRedValue, m_nGreen, m_nBlue); m_Brush.Detach(); // 使画刷和对象分离 m_Brush.CreateSolidBrush(color); // 创建颜色画刷 pDC->SetBkColor( color ); // 设置背景颜色 return (HBRUSH)m_Brush; // 返回画刷句柄,以便系统使此画刷绘制对话框 }
124
3.3常用控件 代码中,COLORREF是用来表示RGB颜色的一个32位的数据类型,它是Visual C++中一种专门用来定义颜色的数据类型。(画刷的详细用法以后还会讨论) (4)编译运行并测试。 需要说明的是: ● 由于滚动条和滑动条等许多控件都能产生WM_HSCROLL或WM_VSCROLL消息,因此当它们是处在同一方向(水平或垂直)时,就需要添加相应代码判断消息是谁产生的。 ● 由于滚动条中间的滚动块在默认时是不会停止在用户操作的位置处的,因此需要调用SetScrollPos函数来进行相应位置的设定。
125
3.4通用对话框和消息对话框 通用对话框 Windows提供了一组标准用户界面对话框,它们都有相应的MFC库中的类来支持。用户或许早已熟悉了全部或大部分的这些对话框,因为众多的Windows的应用程序早已使用过它们,其中包括Visual C++。所有这些通用对话框类都是从一个公共的基类CCommonDialog派生而来。表3.22列出了这些通用对话框。 表3.22 MFC的通用对话框 对话框 用 途 CColorDialog 颜色对话框,允许用户选择或创建颜色 CFileDialog 文件对话框,允许用户打开或保存一个文件 CFindReplaceDialog 查找替换对话框,允许用户查找或替换指定字符串 CPageSetupDialog 页面设置对话框,允许用户设置页面参数 CFontDialog 字体对话框,允许用户从列出的可用字体中选择一种字体 CPrintDialog 打印对话框,允许用户设置打印机的参数及打印文档
126
3.4通用对话框和消息对话框 用户可以在程序中直接使用这些通用对话框,例如下列代码运行后将弹出如图3.55所示的对话框。选定一个文件后,单击[打开]按钮,就会弹出一个消息对话框,显示该文件的全路径名称。 图3.55 通用文件对话框
127
3.4通用对话框和消息对话框 CString filter; 通用文件对话框类CFileDialog的构造函数的原型如下:
filter = "文本文件(*.txt)|*.txt|C++文件(*.h,*.cpp)|*.h;*.cpp||"; CFileDialog dlg (TRUE, NULL, NULL, OFN_HIDEREADONLY, filter); if (dlg.DoModal () == IDOK) { CString str = dlg.GetPathName(); AfxMessageBox(str); } 通用文件对话框类CFileDialog的构造函数的原型如下: CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, PCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );
128
3.4通用对话框和消息对话框 参数中,lpszDefExt用来指定文件扩展名。若用户在文件名编辑框中没有键入扩展名,则系统在文件名后自动添加lpszDefExt指定的扩展名。lpszFileName用来在文件名编辑框中指定开始出现的文件名,若为NULL时,则不出现。dwFlags用来指定对话框的界面标志,当为OFN_HIDEREADONLY时表示隐藏对话框中的“只读”复选框,当为OFN_OVER- WRITEPROMPT时表示文件保存时,若有指定的文件有重名,则出现提示对话框。pParentWnd用来指定对话框的父窗口指针。lpszFilter参数用来确定出现在文件列表框中的文件类型。它由一对或多对字符串组成,每对字符串中第一个字符串表示过滤器名称,第二个字符串表示文件扩展名,若指定多个扩展名则用“;”分隔,字符串最后用两个“|”结尾。注意:字符串应好写在一行,若一行写不下则用“\”连接。 上述代码中,AfxMessageBox用来弹出一个消息对话框(后面还要讨论)。 GetPathName是CFileDialog类成员函数,用来获取文件的全路径名。CFileDialog中类似这样的成员函数还有很多,例如: CString GetFileName( ) const; 该函数返回在对话框确定的文件名;如确定的文件是“C:\FILES\TEXT.DAT”则返回“TEXT.DAT” CString GetFileExt( ) const; 该函数返回在对话框确定的文件扩展名;如确定的文件是“DATA.TXT”,则返回“TXT”。 值得注意的是:只有当调用对话框类的成员函数DoModal并返回IDOK后,该对话框类的这些属性成员函数才会有效。
129
3.4通用对话框和消息对话框 消息对话框 消息对话框是最简单的一类对话框,它只是用来显示信息的。在Visual C++ 6.0的MFC类库中就提供相应的函数实现这样的功能,使用时,直接在程序中调用它们即可。它们的函数原型如下: int AfxMessageBox( LPCTSTR lpszText, UINT nType = MB_OK, UINT nIDHelp = 0 ); int MessageBox( LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, UINT nType = MB_OK ); 这两个函数都是用来创建和显示消息对话框的,它们和前面示例Ex_HelloMsg使用到的Win32 API函数MessageBox是不同的,前者是MFC类库中的函数。AfxMessageBox是全程函数,可以用在任何地方。而MessageBox只能控件、对话框、窗口等一些窗口类中使用。 这两个函数都返回用户选择按钮的情况,其中IDOK表示用户单击[OK]按钮。参数lpszText表示在消息对话框中显示的字符串文本,lpszCaption表示消息对话框的标题,为NULL时使用默认标题,nIDHelp表示消息的上下文帮助ID标识符,nType表示消息对话框的图标类型以及所包含的按钮类型,这些类型是用MFC预先定义的一些标识符来指定的,例如MB_ICONSTOP、MB_YESNOCANCEL等,具体见表3.23和3.24。
130
3.4通用对话框和消息对话框 表3.23 消息对话框常用图标类型 表3.24 消息对话框常用按钮类型 图标类型 含 义
表3.23 消息对话框常用图标类型 图标类型 含 义 MB_ICONHAND、MB_ICONSTOP、 MB_ICONERROR 用来表示 MB_ICONQUESTION MB_ICONEXCLAMATION、MB_ICONWARNING MB_ICONASTERISK、MB_ICONINFORMATION 表3.24 消息对话框常用按钮类型 按钮类型 含 义 MB_ABOUTRETRYIGNORE 表示含有“关于”、“重试”、“忽略”按钮 MB_OK 表示含有“确定”按钮 MB_OKCANCEL 表示含有“确定”、“取消”按钮 MB_RETRYCACEL 表示含有“重试”、“取消”按钮 MB_YESNO 表示含有“是”、“否”按钮 MB_YESNOCANCEL 表示含有“是”、“否”、“取消”按钮
131
3.4通用对话框和消息对话框 在使用消息对话框时,图标类型和按钮类型的标识可使用按位或运算符“|”来组合,例如下面的代码框架中,MessageBox将产生如图3.56所示的结果。 int nChoice = MessageBox("你喜欢Visual C++吗?","提问", MB_OKCANCEL|MB_ICONQUESTION); if (nChoice == IDYES) { //... } 图3.56 消息对话框
132
3.5综合应用 如图3.57(a)所示,列表框内容用来显示一个学生信息的记录,包括:班级、姓名、学号、性别、出现年月。单击[更新]按钮,将弹出“学生信息”对话框,如图3.57(b)所示,在该对话框中输入学生的信息,单击[确定]按钮,对话框消失,其信息内容在列表框中更新。 (a) (b) 图3.57 综合应用题图
133
3.5综合应用 要完成本综合应用,则需要: (1)创建一个对话框应用程序,进行图3.57(a)和3.57(b)对话框设计,且为图3.57(b)对话框创建一个对话框类。 (2)在“学生信息”对话框类中,要添加一些代码用于判断相关信息的有效性。 (3)将学生信息按列表项依次添加到列表框中,且在添加前还需对原来的信息进行清除。 (4)下面分3部分过程来实现:创建应用程序、添加对话框、完善代码。 [例Ex_A3] 第三章综合应用 1)创建应用程序 (1)用MFC AppWizard(exe)创建一个默认的对话框应用程序Ex_A3。 (2)将对话框的标题设为“第三章综合应用”。删除“TODO: 在这里设置对话控制。”静态文本控件和[取消]按钮,将[确定]按钮标题改为“退出”。 (3)打开对话框网格,参看图3.57(a)的控件布局,向对话框添加1个列表框控件(保留默认属性,去除“分类(排序)”属性)、1个按钮控件(“标题”设为“更新”,IDC_BUTTON_SET)和静态文本控件。 (4)打开MFC ClassWizard的Member Variables页面,为列表框控件添加Control类型变量m_ListBox,单击[确定]按钮,关闭MFC ClassWizard对话框。
134
3.5综合应用 2)添加对话框 (1)添加一个新的对话框资源,通过其属性对话框,将ID标识符改为IDD_INFO,标题为“学生信息”,将对话框字体改为“宋体,9号”。 (2)显示对话框网格,调整对话框的大小,然后将[确定]和[取消]按钮移至对话框的下方。 (3)向对话框添加如表3.25所示的控件,参照图3.57(b)调整控件的位置。 表3.25 学生信息对话框添加的控件 添加的控件 ID标识符 标 题 其他属性 编辑框(班级) IDC_EDIT_CLASS —— 默认 编辑框(姓名) IDC_EDIT_NAME 编辑框(学号) IDC_EDIT_NO 单选按钮(男) IDC_RADIO_M 男 单选按钮(女) IDC_RADIO_W 女 日期时间控件 IDC_DATETIMEPICKER1
135
3.5综合应用 (4)双击对话框模板空白处或按Ctrl+W键,为该对话框模板创建对话框类CInfoDlg。
(5)在MFC ClassWizard的Member Variables页面中,确定Class name中是否已选择了CInfoDlg,选中所需的控件ID标识符,双击鼠标或单击Add Variables按钮。依次为表3.26控件增加成员变量。 表3.26 控件变量 控件ID标识符 变量类别 变量类型 变量名 范围和大小 IDC_EDIT_CLASS Value CString m_strClass 20 IDC_EDIT_NAME m_strName 20 IDC_EDIT_NO m_strNO DC_DATETIMEPICKER1 CTime m_tBirth ——
136
3.5综合应用 (6)用MFC ClassWizard为CInfoDlg添加WM_INITDIALOG消息映射,添加下列代码:
BOOL CInfoDlg::OnInitDialog() { CDialog::OnInitDialog(); // 设置默认的性别为男 CheckRadioButton(IDC_RADIO_M, IDC_RADIO_W, IDC_RADIO_M); return TRUE; // return TRUE unless you set the focus to a control } (7)为CInfoDlg类添加一个CString成员变量m_strSex。 (8)用MFC ClassWizard为CInfoDlg的[确定]按钮IDOK增加BN_CLICKED消息映射,并添加下列用于判断输入输入信息有效性的代码:
137
3.5综合应用 3)完成代码 (1)用MFC ClassWizard为CEx_A3Dlg对话框中的“更新”按钮IDC_BUTTON_SET增加BN_CLICKED消息映射,并添加下列代码: void CEx_A3Dlg::OnButtonSet() { CInfoDlg dlg; if ( IDOK != dlg.DoModal() ) return; m_ListBox.ResetContent(); // 清空列表框内容 m_ListBox.AddString( dlg.m_strClass ); // 添加班级 m_ListBox.AddString( dlg.m_strName ); // 添加姓名 m_ListBox.AddString( dlg.m_strNO ); // 添加学号 m_ListBox.AddString( dlg.m_strSex ); // 添加性别 // 添加出生年月 m_ListBox.AddString( dlg.m_tBirth.Format("%Y-%m-%d") ); }
138
3.5综合应用 代码中,m_tBirth.Format是引用CTime类的成员函数,用来将一个时间转换成一个字符串,其中“%Y”表示4位数的年号,“%m”表示2位数的月份(01~12),“%d”表示2位数的日(01~31)。 (2)在Ex_A3Dlg.cpp文件的前面添加CInfoDlg类的头文件包含: #include "Ex_A3Dlg.h" #include "InfoDlg.h" (3)编译运行并测试。 总之,对话框是MFC应用程序最常见也是最有用的一种界面,对话框中常布局一些控件,用来完成相应的数据输入以及其它一些操作。但作为一个完整的文档应用程序来说,还应有菜单栏、工具栏和状态栏等一些界面元素,下一章就来讨论。
139
1. 什么是对话框模板、对话框资源和对话框类? 2. 对一个对话框编程一般经过几个步骤? 3. 什么是模式和无模式对话框?它们有哪些不同?
习题 1. 什么是对话框模板、对话框资源和对话框类? 2. 对一个对话框编程一般经过几个步骤? 3. 什么是模式和无模式对话框?它们有哪些不同? 4. 通用对话框有哪些?调用时需要注意哪些要点? 5. 什么是静态控件?静态控件有哪些? 6. 什么是按钮控件?按钮控件有哪些? 7. 一个对话框中有9个单选按钮控件,分为3组,每组3个,每组中只能有一个单选按钮被选中。先设计这个对话框,然后编程获得每组选中的单选按钮控件的文本内容。 题8图 8. 制作一个“用户登录”对话框,如右图所示。当用户输入“用户名”和“密码”分别是“LiMing”和“886688”时,显示“输入正确!”,否则显示“没有此用户名!”或“密码错误!”。[提示] 选中“密码”编辑框的密码(Password)属性。 9. 什么是旋转按钮的“伙伴”控件?如何设置? 10. 在对话框中添加一个编辑框和旋转控件,并使它们成为伙伴窗口。设编辑框默认的数值为29.7,当单击旋转控件的向上和向下按钮时分别使编辑框数值按0.1增加和减少。 11. 在例Ex_Timer中,若添加一个按钮,单击此按钮后时间才会自动显示,则如何应如何修改代码?
Similar presentations