
对话框是一种用户界面,几乎每一个 Windows 程序都使用对话框与角户进行交互。对话框可能是一个简单的只含有 OK 按钮的消息框,也可以是一个复杂的数据输入表单。对话框上有一些方便用户输入的控件,对话框依靠这些控件与用户进行交互,其实质是一个接收消息的窗口。
本章主要讲解模式对话框和无模式对话框的原理和使用方法,下一章将详细介绍各种控件的使用。你将学习创建一个对话框,使用对话框模板编辑一个对话框资源,并在程序中调用对话框。
在创建一个对话框之前,先来了解一下对话框是如何工作的。对话框的数据来自三方面——对话框资源、对话框对象和一个文档对象。
1 .对话框资源
对话框资源是一个用户输入或取得数据的图形界面。这个图形界面是使用 Develop Studio 的对话框编辑器在对话框模板上创建的,程序员可以在对话框模板上增加并编辑控件,生成对话框资源。当应用程序运行时,就可以得到一个对话框。
2 .对话框对象
MFC 使用 CDialog 类来描述对话框,它是 CWnd 类的派生类。在 CWnd 类的基础上增加了数据交换的功能。当创建一个新的对话框资源后,使用 C1assWizard 可以创建一个对话框类的派生类。对话框对象实现了对话框和应用程序之间的通信。在应用程序中定义一个对话框对象后,在对话框显示前,可以通过访问对话框对象的成员变量为该对话框的控件初始化,在对话框关闭后,可以通过访问对话框对象的成员变量获得用户的输入数据。
3.文档对象
MFC 使用文档对象描述应用程序处理的数据,用户输入的数据要进一步的处理,通常要先保存到文档对象中。例如一个处理学生记录的应用程序,用户通过一个对话框输出学生记录并保存到一个文件中。应用程序的处理流程是:用户在对话框中输入信息,程序通过对话框对象得到输入信息,将输入信息整理到文档对象中,使用序列化函数将文档对象保存到一个文件中,所以文档对象扮演了一个很重要的数据交换的角色。
MFC 通过以上三者实现用户与应用程序之间的数据交换。
对话框分为模式对话框和无模式对话框两种。
当一个模式对话框打开后,在其关闭之前,用户不能转向其他用户界面对象,而只能与该对话框进行交互。我们平时接触到的对话框,大多数都是模式对话框,例如,选择菜单项 File | Save 后, Save 对话框被打开,用户不能再做其他工作,只有保存完文件或取消保存文件、关闭对话框窗口后,才能做其他工作。
无模式对话框恰恰相反,当用户打开一个无模式对话框时,对话框停留在屏幕上,仍然允许用户与其他用户界面进行交互。最典型的无模式对话框是在 Word 中使用的查找与替换对话框,打开该对话框后,可以交替地进行文档编辑和查找替换操作。
对话框的设计任务主要包括对话框资源模板的设计、对话框类的设计和对话框的运行。对于绝大多数对话框的创建任务,模式对话框和无模式对话框都是相同的,只有在对话框与应用程序代码的交互中,也就是对话框的运行中,两者有明显区别。
二、设计对话框
1、创建对话框:
使用 AppWizard 生成的单文档或多文档应用程序,只有一个默认的 About 对话框,用于显示应用程序版本信息。需要使用对话框模板创建自己的对话框资源。
Develop Studio 提供的对话框模板创建了一个基本界面,包括一个 OK ‘确定)按钮和一个 Cancel (取消)按钮等。你可以移动、修改、删除这些控件,或者是增加新的控件到对话框模板,构成应用程序所需的对话框资源。
创建一个对话框资源到项目的步骤如下:
(1)使用 AppWizard 生成一个单文档的 MFC 应用程序 Exam5_1
(2) 选择 Insert | Resource 菜单项。
(3)在弹出的 Insert Resource 对话框左边的列表中选择 Dialog, 单击 New 按钮。
(4)在项目工作区中选择 Resource View 面板,展开 Dialog 文件夹,可以看到增加了一个对话框资源 IDD_DIALOG1, 如图所示,双击 IDD DIALOGI ,在编辑区出现一个对话框模板。
2、增加控件
控件能放置在一个对话框中,提供应用程序与用户交互的某种功能。 VC++6.0 提供很多标准控件,我们可以方便地从控件工具栏中添加这些控件。
3、设置控件属性
包括控件的 ID 设置、 Caption 设置等。
4、组织和安排控件
5、测试对话框
方法有:( 1 )选择菜单项 Layout|test
( 2 )单击 Dialog 工具栏上的 test 按钮。
( 3 )按快捷键 Ctrl+T 。
三、设计对话框类
一个对话框资源要加入到一个 Windows 应用程序中,首先要为它创建一个对话框类,即 CDialog 的派生类。应用程序运行时, Windows 操作系统使用对话框类为对话框资源分配内存空间。
1、创建对话框类:
在 Win32 API 类型的 Windows 应用程序中,都是直接通过控件 ID 来访问对话框上的控件,而 MFC 提供了连接变量来标识控件,连接变量是对话框类的成员变量,同时对话框类有一套交换和验证数据的方式。
创建对话框类的步骤如下:
(1) 保存己创建的对话框资源。
(2)确保新的对话框资源在对话框编辑器中处于打开状态,打开 C1assWizard 窗口。
打开 C1assWizard 的方式有下面 3 种:
①选择窗口菜单项 View | ClassWizard 。
②选择快捷菜单项 C1assWizard 。
③按快捷键 Ctrl+W 。
(3)在弹出的 Adding a Class 对话框中,选择 Create a new class 单选钮,单击 OK 按钮。
(4) 在随后弹出的 New Class 对话框中,只需填写类的名称 CscoreDlg 。单击 OK 按钮,关闭 ClassWizard ,对话框类的创建就完成了。
在 Workspace 窗口的 C1assView 面板中,可以看到增加了一个新的类 CScoreDlg, 选择 FileView 面板,在 Header Files 和 Source Files 文件夹中,可以看到该类的头文件和实现文件,文件名是类名除去开头的类标志“ C ”。
在创建新类的时候,要注意新类的对话框 ID 一定要与对话框资源 ID 一致。这些 ID 告诉对话框类,在对话框窗口创建前要检查哪一个对话框资源,初始化控件并显示该对话框。尤其在创建对话框资源后,不能随意地改动对话框资源 ID 和删除对话框资源。
2、创建对话框成员变量
创建一个对话框类后,可以增加类的成员变量来操作对话框上的的控件。出于不同的操作目的, MFC 提供了两种类型的成员变量。
• Values 类型:是值类型的成员变量,用于控件的值控制。
• Controls 类型:控件类型的成员变量,实质是该控件类的一个对象。
3、对话框数据交换和校验
对话框的值类型的成员变量存储了与控件相对应的数据,对话框打开时,用户可以修改控件的数据,有时需要应用程序对用户的输入进行及时反馈,这时,数据成员变量需要与控件交换数据,以完成输入输出功能。对于这种功能, MFC 是靠 CDataExchange 类提供的数据交换〔 DDX, dialog data exchange) 机制来完成的,该类还提供了数据有效化机制( DDV, dialog data validation )。
在对话框类中增加一个数据成员变量,有时需要规定数据的有效性校验规则,例如,成绩录入对话框中设置各门课的成绩应在 0-100 的范围内。在应用程序运行的时候,当用户输入数据复制到数据成员变量时,调用 DDV 机制,如果有效性校验失败,即用户输入一个不合理的数据时,将出现一个错误信息对话框,并返回到出现错误输入的控件。
有效性校验的设置也是在 C1assWizard 中完成的。回到成绩录入的 Member Variables 标签页,选择表示成绩输入框的变量 m scorel ,在标签页的下方会出最大值和最小值的输入框,如下图所示,填入0和 100 。一个 CString 型的数据成员变量对应的通常是字符串的长度。
四、运行对话框
创建一个对话框资源和对话框类之后,要在应用程序中运行对话框,通常要通过一个菜单项下打开该对话框。所以,首先要定义一个菜单项和相应的菜单命令消息处理函数,在该函数中编写代码运行对话框。
运行模式对话框和无模式对话框有所不同,下面结合实例详细介绍两种对话框运行的方法。
在应用程序中使用得较多的是模式对话框,模式对话框的运行分两个步骤:
(1) 创建一个对话框对象。
(2)调用 CDialog::DoModal( )函数打开对话框。
DoModal( )函数负责模式对话框的创建和撤消。在创建对话框时, DoModal ()的任务包括装载对话框资源、调用 OnlnitDialog( )初始化对话框并将对话框显示在屏幕上。完成对话框的创建后, DoModal ()启动一个消息循环,以响应用户的输入。这时,用户只能与该对话框进行交互,其他用户界面对象收不到输入信息,只有终止该模式对话框后才能进行其他工作。
若用户在对话框内单击了系统默认生成的 OK( 确定)按钮,则将调用 CDialog::OnOK( )函数, OnOK( )首先调用 UpdateData (true) 函数,将数据从对话框中的控件复制到数据成员变量,再调用 CDialog::EndDialog( )关闭对话框,并返回值 1DOK 。
若用户在对话框中单击了系统默认生成的 Cancel (取消)按钮,则将调用 CDialog::OnCancel( )函数,该函数只调用 CDialog::EndDialog( )关闭对话框,并返回值 IDCANCEL 。程序可以根据返回值是 IDOK 还是 IDCANCEL 来判断用户关闭对话框时按的哪一个键。
为此,要定义对应的文档对象。步骤如下:
(1) 为文档类增加公有变量并设置初值
定义 int 型成员变量数组 score[5], 保存 5 门课程成绩; int 型成员变量 TScore, 用来保存总分。
在类构造函数中设置数组 score 和 TScore 的初值都为 0 。
(2)在视图类 OnDraw 函数中添加代码,实现成绩的输出
void CExam5_1View::OnDraw(CDC* pDC)
{
CExam5_1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->TextOut(0,0," 高等数学 大学物理 大学英语 哲学 计算机基础 总分 ");
CString str;
str.Format("%10d%16d%16d%16d%16d%18d",pDoc->score[0],pDoc->score[1],pDoc->score[2],
pDoc->score[3],pDoc->score[4],pDoc->TScore);
pDC->TextOut(0,20,str);
}
(3) 增加一个菜单项
在“编辑”菜单的最下面增加一个菜单项, Caption 设为“成绩录入”,菜单 ID 为 ID_ SCOREFILLING 。
(4)为新增菜单项在文档类定义消息处理函数
用 CIassWizard 在文档类生成菜单命令消息处理函数 OnScorefilling( ) 。
(5)编写代码完成下面的功能
①创建对话框对象。
②运行成绩录入对话框,并把对话框数据成员的值复制到文档类成员变量 score 数组中。
③计算总分。
④更新视图。
void CExam5_1Doc::OnScorefilling()
{
// TODO: Add your command handler code here
CScoreDlg dlg;
if(dlg.DoModal()==IDOK)
{
score[0]=dlg.m_score1;
score[1]=dlg.m_score2;
score[2]=dlg.m_score3;
score[3]=dlg.m_score4;
score[4]=dlg.m_score5;
for(int i=0;i<5;i++)
TScore+=score[i];
this->UpdateAllViews(NULL);
}
}
(6)加入头文件
在 Exam5_1Doc.cpp 文件头部的所有 include 语句之后加入语句 #include “ScoreDlg.h”
(7)编译运行
选择菜单项“编辑 | 成绩录入”,弹出“成绩录入”对话框,输入各门课程的成绩后,单击“确定”按钮退出对话框,应用程序主窗口中显示刚刚输入的各课程成绩和总分。
• 无模式对话框
无模式对话框与模式对话框的不同在于,用户打开无模式对话框后,仍然可以与其他用户界面元素交互。创建无模式对话框的过程与模式对话框基本相同,都包括对话框资源的创建和对话框对象的创建,但在运行对话框的设计上存在着差异。
(1) Visible 属性
在对话框的属性对话框的 More Styles 标签页中,有 Visible 属性的设置,默认情况下,对话框模板是不选择 Visible 属性。模式对话框不需要设置该属性,而无模式对话框没有选择该属性,窗口创建后,对话框是不显示的,需要调用 CWnd::ShowWindow ( SW SHOW) 来显示对话框。
(2) 创建方式
由于无模式对话框对象在程序运行时可以一直存在,所以不能以局部变量的形式创律,只能用 new 操作符动态创建,并且在调用对话框类的窗口类内声明一个指向对话框类的指针变量,通过指针访问对话框对象。
(3) 窗口创建方式
无模式对话框的创建与普通窗口的创建是一样的,通过调用 CWnd::Create ()函数来创建对话框。由于 Create( ) 函数不会启动新的消息循环,即对话框与应用程序共用一个消息循环,这样对话框就不会屏蔽用户对其他界面对象的访问。 Create( ) 函数与 DoModal( )函数不同之处是: Create ()创建了对话框后立即返回,而 DoModal ()函数要在对话框关闭后才会返回。
(4)窗口删除函数
无模式对话框必须调用 CWnd::DestoryWindow( ) 来关闭对话框。模式对话框是调用 CDialog::EndDialog( )关闭对话框。由于默认的对话框函数 OnOK( )和 OnCancel( )都是调用 EndDialog() 关闭对话框的,所以无模式对话框类要定义自己的 OnOK( ) 和 OnCancel() 函数,调用 DestoryWindow( )来关闭对话框。
(5)清理对话框对象的方式
与创建对象的方式 new 操作相对应,使用 delete 操作删除一个无模式对话框对象。当屏幕上一个窗口被关闭后,框架会自动调用 CWnd::PostNcDestroy( )函数,可以编写程序代码,在这个函数中清理无模式对话框对象。
(6) 无模式对话框不能重入
因为在无模式对话框打开的情况下,用户有可能再次选择对应菜单项、打开该对话框,这时不能再创建一个新的无模式对话框。所以程序在用户选择菜单项后,应该能够判断是打开一个新的对话框还是激活一个己打开的对话框。通常可以使用对话框对象指针来判断。对话框对象指针始终指向一个对话框对象,当关闭对话框时,设置指针的值为 NULL 。
例如:通过下列操作可以在项目 Exam5_1 中增加一个画圆的无模式对话框。
(1)增加一个对话框模板
通过菜单项 Insert|Resource, 增加一个新的对话框模板 IDD DIALOG2 ,增加相应控件并设置属性。
(2) 创建对话框类
打开 C1assWizard, 创建一个新的对话框类 CModelessDlg, 并按下表所示创建该对话框类的数据成员变量。
控件 ID 变量名称 分类 数据类型
IDC_EDIT 1 m _x Value int
IDC_EDIT 2 m _y value int
IDC_EDIT 3 m _radius value int
(3) 定义消息处理函数,重载虚函数
使用 C1assWizard 为 CModelessDlg 创建成员函数。
Objects IDs Message 备注
IDOK BN_CLICKED 消息处理函数
IDCANCEL BN_CLICKED 消息处理函数
CmodelessDlg PostNcDestroy 虚函数
(4)定义对话框指针
①在 Cexam5_1 View 视图类中增加 CModelessDlg 指针类型的公有成员变量 m-pDlg 。
②在 Exam5_1View.h 文件的头部增加类的前向声明语句如下:
class CModelessDlg ;
class Cexam5_1Doc ;
类的前向声明语句的作用为:由于在 Cexam5_1View 类中有一个 CModelessDlg 类的指针和一个返回值为 Cexam5_1Doc 指针的 GetDocument( )函数,因此必须保证 CModelessDlg 类和 Cexam5_1Doc 类的声明出现在 Cexam5_1View 之前,否则会产生编译错误。
(5) 增加调用对话框的菜单项
①在“查看”菜单的最下面增加一个菜单项, ID 为 ID_CIRCLE, Caption 为“画圆”
②使用 C1assWizard 为该菜单项在 Cexam5_1 View 视图类中映射命令处理函数
Cexam5_1 View::OnCircle( )。
(6)增加对话框类成员变量接收视图指针
①在 CModelessDlg 类中增加 Cexam5_1 View 指针类型的公有成员变量 m_parent 。
“设置”对话框打开时,只要单击“画圆”按钮,就可以在主窗口视图上画一个圆,所以在对话框上单击“画圆”按钮的事件消息处理函数中要能获得主窗口视图的指针,并创建一个指向主窗口视图的 CClientDc 对象实现画圆。
②在 ModelessDlg.h 文件的头部预编译命令之前增加类的前向声明语句如下:
class Cexam5_1View;
(7)定义头文件
在 Exam5_1 View.cpp 文件的头部所有的 include 语句之后添加 include 语句如下,将对话框类的头文件包含进来。
# include "ModelessDlg.h"
在 ModelessDialog.cpp 文件的头部所有的 include 语句之后添加 include 语句如下,将视图类的头文件包含进来。
#include “Exam6_lView . h”
(8) 实现视图类中的成员函数
①在构造函数中设置 m_pDlg 的初值为 NULL 。
②在菜单命令消息处理函数 OnCircle( )中判断该对话框是否已打开,若已打开则激活对话框,否则创建一个新的对话框。
五、 通用对话框
(一)通用对话框的类型
MFC 的通用框库 (CommDlg.dll) 包含一套应用程序通用的对话框,可供程序员方便地加入到一个应用程序。
CColorDialog : 颜色对话框
CFileDialog: 文件对话框
CFindReplaceDialog 查找 / 替换对话框
CFontDialog 字体对话框
CPrintDialog 打印对话框
CpagesSetupDialog 页面设置对话框
(二)使用通用对话框
使用通用对话框的步骤:
• 创建一个通用对话框类的对象。
• 修改相应对话框,调用 DoModal() 函数打开对话框。
• 如果是模式对话框,调用 DoModal() 函数打开对话框。
(三)编程实例:
修改应用程序 Exam5_1 ,在无模式对话框中增加功能,可以使用颜色对话框设置圆的填充颜色。步骤:
• 为 CModelessDlg 类增加 COORREF 类型的公有成员变量 m_color ,用以记录圆的填充颜色。
• 在 CModelessDlg 类构造函数中添加如下语句:初始设置圆的填充色为白色。
m_color=RGB(255,255,255);
• 修改对话框资源 IDD_DIALOG2 ,增加一个命令内按钮, Capition 为“ Color ”, ID 为 ID_COLOR 。
• 为 Color 按钮在 CModelessDlg 类映射 EN_CLICKED 事件的消息处理函数 OnColor() 。
• 为 OnColor() 函数添加代码,实现颜色对话框的调用。
• 修改OnOk()函数,设置画刷颜色实现填充。
