对话框有两种类型:模态(Modal)对话框 和 非模态(Modeless)对话框。
模态对话框
模态对话框是指当其显示时,程序会暂停执行,直到关闭这个模态对话框后,才能继续执行程序中的其它任务。举个例子来说,你打开一个记事本,然后点击”帮助“菜单里的”关于记事本“按钮,这时会弹出一个描述记事本信息的对话框,这个对话框就是模态对话框。在你关闭该对话框之前,你无法在记事本里录入内容,如你点击记事本的录入区域时,系统会发出”咚咚“的提示声,同时也看到”关于“对话框的标题栏上不断的闪烁着。模态对话框垄断了用户的输入,当一个模态对话框打开时,用户只能与该对话框进行交互,而其它用户界面对象接收不到输入信息。我们平时遇到的大部分对话框是模态对话框。
非模态对话框
当非模态对话框显示时,允许转而执行程序中其它的任务,而不用关闭这个对话框。比如记事本中的”查找“对话框。该对话框不会垄断用户的输入,打开”查找“对话框后,仍可以与其他用户界面对象进行交互,用户可以一边查找,一边修改记事本内容,这样就大大方便了使用。
创建模态对话框(VS2010)
先创建一个基本的 MFC 工程,然后点击“资源视图”,再展开 Dialog 文件夹:
默认情况下,在上图可以看到已经自带有两个对话框:IDD_ABOUTBOX 和 IDD_MODEL_DIALOG ,点开后可以看到一个是 ABOUT 对话框,另一个是主界面对话框:
2. 现在插入一个对话框。
插入对话框有两种方式,一种是右键点击 "Dialog" 文件夹,在弹出的菜单中选择“插入 Dialog”;另一种是在弹出的菜单中选择“添加资源”,然后再选择“Dialog”也行。
在上面的这个对话框中,有两个按钮,分别是“确定”和“取消”。可以分别单击这两个按钮,在 IDE 的右侧的“属性” 对话框中可以看到它们的 ID 分别为 IDOK 和 IDCANCEL 。VC++ 已经为这两个按钮提供了默认的消息响应函数 OnOK 和 OnCancel ,它们实现的主要功能都是一样的,就是关闭对话框。因此,在程序运行时,单击这两个按钮中的任何一个都可以关闭对话框。但是,单击这两个按钮关闭对话框后的返回结果是不一样的。在程序中,通常根据该返回值来判断用户单击了哪个按钮,从而确定用户的行为:是“确定”还是“取消”当前操作。
注意到这个新插入的对话框的标题名称是“Dialog”,我们可以通过在它的属性对话框的”外观“的"caption"这一栏里修改其名称,比如修改为 "测试" :
在下面的描述中,该对话框统称为”测试对话框“。
在 MFC 中,对资源的操作通常都是通过一个与资源相关的类来完成的。对话框资源也有一个相应的基类:CDialog 。根据 MSDN 可了解到,CDialog 类派生于 CWnd 类,所以它是一个与窗口相关的类,主要用来在屏幕上显示一个对话框。由此可知,实际上,对话框本身也是一个窗口界面。
既然在 MFC 中对资源的操作是通过一个类来完成的,那么就需要创建一个类与这个新建对话框资源相关联。我们可以通过右键点击该对话框,在弹出的菜单中选择”添加类“,也是就会进入到”MFC 添加类向导“:
上面,我们可以自定义一个类名,选择”基类“为 ”CDialog“,然后系统会自动生成相应的 .h 和 .cpp 文件名:
点击”完成“即可。
我们在生成的源文件 TestDialog.cpp 中可以看到该类的构造函数:
[C++] 纯文本查看 复制代码 CTestDialog::CTestDialog(CWnd* pParent /*=NULL*/)
: CDialog(CTestDialog::IDD, pParent)
{
}
从上面可以知道,CTestDlg 类的构造函数首先调用其基类 CDialog 的构造函数,并传递了两个参数:一个是 CtestDialog 类的 IDD 成员,一个是父窗口的指针 pParent 。打开 CtestDialog 类的头文件可以看到这个 IDD 就是这个对话框的资源 ID ( enum { IDD = IDD_DIALOG1 }; )。
CTestDialog 类的另一个函数是:DoDataExchange ,它主要用来完成对话框数据的交换和校验,其定义如下所示:
[C++] 纯文本查看 复制代码 void CTestDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
到现在为止,我们就有了一个类(CTestDialog)与 IDD_DIALOG1 这个对话框相关联了(这种关联的行为也可以称为”绑定“),就像程序中的 CAboutBox 这个对话框资源与类 CAboutDlg 相关联一样。
接下来,我们希望在程序中显示这个对话框窗口。首先,在主程序对话框中拉入一个按钮控件:
双击一下该按钮,来到对应的处理函数处,编写下面的代码,即可建立起一个模态对话框:
[C++] 纯文本查看 复制代码 void CmodelDlg::OnBnClickedButton1()
{
CTestDialog dlg;
dlg.DoModal();
}
非模态对话框使用 Create 方法来创建。Create 方法有两种重载形式:
[C++] 纯文本查看 复制代码 virtual BOOL Create(
LPCTSTR lpszTemplateName,
CWnd* pParentWnd = NULL
);
virtual BOOL Create(
UINT nIDTemplate,
CWnd* pParentWnd = NULL
);
一般情况下,我们会用第二种。在第 2 种形式中,第 1 个参数就是模板 ID ,第 2 个参数是个父窗口指针。
在创建非模态对话框时,最容易发生的错误是“一闪而过”,比较下面代码:
[C++] 纯文本查看 复制代码 void CmodelDlg::OnBnClickedButton1()
{
CTestDialog dlg;
dlg.Create(IDD_DIALOG1, this);
dlg.ShowWindow(SW_SHOW);
}
在上面的代码中,在使用 Craete 创建完对话框后,需要用 ShowWindow 来显示窗口。但在运行程序,并点击了按钮后,并没有发现有窗口弹出,是不是创建失败了呢?答案是否定的,创建仍然是成功的,只不过“一闪而过”而已。我们可以在添加一个 MessageBox(_T("hello world")); 语句来验证,这时可以看到:
Messagebox 弹了出来,当你点击 messagebox 上的确定按钮后,非模态窗口也跟着退出。
上面一闪而过的原因是声明的 dlg 这个对象是个局部变量,在 ShowWindow 之后,它也就跟着退出了。那有可能会问,非模态窗口没有用 ShowWindow,为什么可以显示对话框?原因是 Domodal 函数本身就有显示模态对话框的作用,因此它不需要调用 ShowWindow 函数。那非模态窗口不也同样是局部变量而没退出么?答案是 DoModal 函数是个阻塞型函数,它在关闭模态窗口之前不会退出,因此它不存在“一闪而过”的问题。
解决非模态窗口一闪而过的问题,可以考虑将 dlg 声明为全局变量。也可以在主窗口头文件的类定义里添加一个 CTestDialog 类的指针:CTestDialog *pDlg; 。然后修改按钮处理函数里的代码:
[C++] 纯文本查看 复制代码 pDlg = new CTestDialog;
pDlg->Create(IDD_DIALOG1, this);
pDlg->ShowWindow(SW_SHOW);
注意,上面使用 new 在堆中分配了内存。我们知道,在堆上分配的内存,与程序的整个生命周期是一致的,当然这里是指程序中不主动销毁的情况。 |