曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 4704|回复: 0
打印 上一主题 下一主题

模态对话框与非模态对话框(新建对话框)

[复制链接]

716

主题

734

帖子

2946

积分

超级版主

Rank: 9Rank: 9Rank: 9

积分
2946
跳转到指定楼层
楼主
发表于 2013-7-25 13:51:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
对话框有两种类型:模态(Modal)对话框 和 非模态(Modeless)对话框。

模态对话框
模态对话框是指当其显示时,程序会暂停执行,直到关闭这个模态对话框后,才能继续执行程序中的其它任务。举个例子来说,你打开一个记事本,然后点击”帮助“菜单里的”关于记事本“按钮,这时会弹出一个描述记事本信息的对话框,这个对话框就是模态对话框。在你关闭该对话框之前,你无法在记事本里录入内容,如你点击记事本的录入区域时,系统会发出”咚咚“的提示声,同时也看到”关于“对话框的标题栏上不断的闪烁着。模态对话框垄断了用户的输入,当一个模态对话框打开时,用户只能与该对话框进行交互,而其它用户界面对象接收不到输入信息。我们平时遇到的大部分对话框是模态对话框。

非模态对话框
当非模态对话框显示时,允许转而执行程序中其它的任务,而不用关闭这个对话框。比如记事本中的”查找“对话框。该对话框不会垄断用户的输入,打开”查找“对话框后,仍可以与其他用户界面对象进行交互,用户可以一边查找,一边修改记事本内容,这样就大大方便了使用。


创建模态对话框(VS2010)

先创建一个基本的 MFC 工程,然后点击“资源视图”,再展开 Dialog 文件夹:


默认情况下,在上图可以看到已经自带有两个对话框:IDD_ABOUTBOX 和 IDD_MODEL_DIALOG ,点开后可以看到一个是 ABOUT 对话框,另一个是主界面对话框:



2. 现在插入一个对话框。
插入对话框有两种方式,一种是右键点击 "Dialog" 文件夹,在弹出的菜单中选择“插入 Dialog”;另一种是在弹出的菜单中选择“添加资源”,然后再选择“Dialog”也行。


在上面的这个对话框中,有两个按钮,分别是“确定”和“取消”。可以分别单击这两个按钮,在 IDE 的右侧的“属性” 对话框中可以看到它们的 ID 分别为 IDOK IDCANCEL 。VC++ 已经为这两个按钮提供了默认的消息响应函数 OnOKOnCancel ,它们实现的主要功能都是一样的,就是关闭对话框。因此,在程序运行时,单击这两个按钮中的任何一个都可以关闭对话框。但是,单击这两个按钮关闭对话框后的返回结果是不一样的。在程序中,通常根据该返回值来判断用户单击了哪个按钮,从而确定用户的行为:是“确定”还是“取消”当前操作。

注意到这个新插入的对话框的标题名称是“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 在堆中分配了内存。我们知道,在堆上分配的内存,与程序的整个生命周期是一致的,当然这里是指程序中不主动销毁的情况。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-4-29 06:12 , Processed in 0.079686 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表