非模态对话框的创建
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
因为是用 new 操作符构建非模态对话框对象,因此必须在对话框关闭后,用 delete 操作符删除对话框对象。在屏幕上一个窗口被销毁(DestroyWindow)后, 框架会调用 CWnd::PostNcDestroy,这是一个虚拟函数,程序可以在该函数中完 成删除窗口对象的工作,具体代码如下:
void CModelessDialog::PostNcDestroy {
void CRegisterDialog::OnCancel() {
((CRegisterView *)GetParent())->pRegisterDlg = NULL; DestroyWindow(); //删除对话框 }
注:非模态对话框的显示问题:
新弹出的非模态子对话框总是显示在主界面的上面。即使当失去焦点时也会 显示在最前,使用很不方便。使用 SetWindowPos 这个函数不能完全解决问题,因 为 SetWindowPos 这个函数一旦设置了其中一个子对话框是 Topmost 那么该子对话 框的父对话框也自动改为 TopMost 属性。所以单单设置主对话框为 TopMost 是不 可以解决问题的。
//GetDesktopwindow()不能少,也不能填 NULL。
pModelessDlg ->ShowWindow(SW_SHOWNA); //想获得焦点就 SW_SHOWNA,否则 SW_SHOW 也可
需要注意的是,当销毁 modelessDlg 时,需要清空 modelessDlg 的指针 pModeless , 为 此 需 要 在 创 建 modeless 时 给 modelessDlg 类 添 加 一 个 变 量 m_pMainDlg 保存 mainDlg 的指针,在 OnOK 函数中添加如下代码:
请读者按下面几步操作:
1. 在登录数据对话框模板的属性对话框的 More Styles 页中选择 Visible 项。
2. 在 RegisterView.h 头文件的 CRegisterView 类的定义中加入
public:
CRegisterDialog* m_pRegisterDlg;
3. 在 RegisterView.h 头文件的头部加入对 CRegisterDialog 类的声明
编译并运行 Register,现在登录数据对话框已经变成一个非模态对话框了
void CRegisterDialog::PostNcDestroy() //在非模态对话框类中重写该虚函数 {
delete this; //删除对话框对象 }
// 如果有 OnOK 函数则需要重写,注意不要调用基类的 OnOK 函数
取消此特性的方法如下:
(1)将 Dialog 的属性“System Modal”(系统模块)设置为 True。
(2)创建代码,将非模态对话框的父窗口设置为桌面:
CDialog* pModelessDlg = new CDialog(NULL);
pModelessDlg->Create(IDD_MODELESSDIALOG,GetDesktopWindow());
5.4 非模态对话框
5.4.1 非模态对话框的特点
与模态对话框不同,非模态对话框不垄断用户的输入,用户打开非模态对话 框后,仍然可以与其它界面进行交互。
非模态对话框的设计与模态对话框基本类似,也包括设计对话框模板和设计 CDialog 类的派生类两部分。但是,在对话框的创建和删除过程中,非模态对话 框与模态对话框相比有下列不同之处:
delete this; //删除对象本身 }
这样,在删除屏幕上的对话框后,对话框对象将被自动删除。拥有者对象就 不必显式的调用 delete 来删除对话Байду номын сангаас对象了。
(5)必须有一个标志表明非模态对话框是否是打开的。这样做的原因是用户有 可能在打开一个模态对话框的情况下,又一次选择打开命令。程序根据标志来决 定是打开一个新的对话框,还是仅仅把原来打开的对话框激活。通常可以用拥有 者窗口中的指向对话框对象的指针作为这种标志,当对话框关闭时,给该指针赋 NULL 值,以表明对话框对象已不存在了。
4. 在 RegisterDialog.cpp 文件的头部的#include 语句区的末尾添加下面两行
#include "RegisterDoc.h"
#include "RegisterView.h"
5. 利用 ClassWizard 为 CRegisterDialog 类加入 OnCancel 和 PostNcDestroy 成 员函数。加入的方法是进入 ClassWizard 后选择 Message Maps 页,并在 Class name 栏中选择 CRegisterDialog。然后,在 Object IDs 栏中选择 IDCANCEL 后,在 Messages 栏中双击 BN_CLICKED,这就创建了 OnCancel。要创建 PostNcDestroy, 先 在 Object IDs 栏 中 选 择 CRegisterDialog , 再 在 Messages 栏 中 双 击 PostNcDestroy 即可。
class CRegisterDialog;
加入该行的原因是在 CRegisterView 类中有一个 CRegisterDialog 类型的指针, 因此必须保证 CRegisterDialog 类的声明出现在 CRegisterView 之前,否则编译 时 将 会 出 错 。 解 决 这 个 问 题 有 两 种 办 法 , 一 种 办 法 是 保 证 在 #include “RegisterView.h”语句之前有#include “RegisterDialog.h”语句,这种办法 造成了一种依赖关系,增加了编译负担,不是很好;另一种办法是在
(1)非模态对话框的模板必须具有 Visible 风格,否则对话框将不可见,而模 态对话框则无需设置该项风格。更保险的办法是调用 CWnd::ShowWindow(SW_SHOW) 来显示对话框,而不管对话框是否具有 Visible 风格。
(2)非模态对话框对象是用 new 操作符在堆中动态创建的,而不是以成员变量 的形式嵌入到别的对象中或以局部变量的形式构建在堆栈上。通常应在对话框的 拥有者窗口类内声明一个指向对话框类的指针成员变量,通过该指针可访问对话 框对象。
(4)必须调用 CWnd::DestroyWindow 而不是 CDialog::EndDialog 来关闭非模 态对话框。调用 CWnd::DestroyWindow 是直接删除窗口的一般方法。由于缺省的 CDialog::OnOK 和 CDialog::OnCancel 函数均调用 EndDialog,故程序员必须编写 自己的 OnOK 和 OnCancel 函数并且在函数中调用 DestroyWindow 来关闭对话框。
调用 CDialog::Create 来显示对话框,该函数的声明为
BOOL Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );
参数 nIDTemplate 是对话框模板的 ID。pParentWnd 指定了对话框的父窗口或 拥有者。以 CDialog 创建的非模态对话框调用 Create 函数时即使你指定父窗口为 NULL,实际的父窗口也不是 NULL,而是你程序的主窗口,所以你指定 NULL 和指
CRegisterView 类的声明之前加上一个对 CRegisterDialog 的声明来暂时“蒙蔽” 编译器,这样在有#include “RegisterView.h”语句的模块中,除非要用到 CRegisterDialog 类,否则不用加入#include “RegisterDialog.h”语句。
5.4.2 窗口对象的自动清除
一个 MFC 窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在 m_hWnd 成员中的 HWND(窗口句柄),二是窗口对象本身是一个 C++对象。要删除 一个 MFC 窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。
m_pMainDlg->m_pDspStateDlg = NULL;
(获取对话框 Dialog 的背景色用 SDK 函数 GetSysColor(COLOR_BTNFACE))
上面的方法有个特性:在任务栏会显示非模态对话框的图标。如果不想在任 务栏显示该图标是不是可以尝试如下方法:可以创建一个隐藏的对话框,然后在 这个对话框上面创建主对话框,把这个主对话框设为 TopMost 就可以了?
定 this 是一样的效果,而指定 GetDesktopWindow()则把非模态窗口的父窗口指 定为桌面了,这时与你程序的主窗口不再有关系。
void CRegisterView::OnEditRegister() // 创建非模态对话框 {
// TODO: Add your command handler code here if(m_pRegisterDlg)
CRegisterView::OnEditRegister 函数判断登录数据对话框是否已打开,若是, 就激活对话框,否则,就创建该对话框。该函数中主要调用了下列函数:
调用 CWnd::SetActiveWindow 激活对话框,该函数的声明为:
CWnd* SetActiveWindow( );
该函数使本窗口成为活动窗口,并返回原来活动的窗口。
m_pRegisterDlg->SetActiveWindow(); //激活对话框 else {
//创建非模态对话框 m_pRegisterDlg=new CRegisterDialog(this); m_pRegisterDlg->Create(IDD_REGISTER,this); } }
例 2: CRegisterDialog 的部分代码
(3) 通 过 调 用 CDialog::Create 函 数 来 启 动 对 话 框 , 而 不 是 CDialog::DoModal,这是模态对话框的关键所在。由于 Create 函数不会启动新的 消息循环,对话框与应用程序共用同一个消息循环,这样对话框就不会垄断用户 的输入。Create 在显示了对话框后就立即返回,而 DoModal 是在对话框被关闭后 才返回的。众所周知,在 MFC 程序中,窗口对象的生存期应长于对应的窗口,也 就是说,不能在未关闭屏幕上窗口的情况下先把对应的窗口对象删除掉。由于在 Create 返回后,不能确定对话框是否已关闭,这样也就无法确定对话框对象的生 存期,因此只好在堆中构建对话框对象,而不能以局部变量的形式来构建之。
当用户在登录数据对话框中点击“取消”按钮后,CRegisterDialog::OnCancel 将 被 调 用 , 在 该 函 数 中 调 用 CWnd::DestroyWindow 来 关 闭 对 话 框 , 并 且 将 CRegisterView 的成员 m_pRegisterDlg 置为 NULL 以表明对话框被关闭了。调用 DestroyWindow 导致了对 CRegisterDialog::PostNcDestroy 的调用,在该函数中 用 delete 操作符删除了 CRegisterDialog 对象本身。
提示:在 C++编程中,判断一个位于堆中的对象是否存在的常用方法是判断指向 该对象的指针是否为空。这种机制要求程序员将指向该对象的指针初始化为 NULL 值,在创建对象时将返回的地址赋给该指针,而在删除对象时将该指针置成 NULL 值。
根据上面的分析,我们很容易把 Register 程序中的登录数据对话框改成非模 态对话框。这样做的好处在于如果用户在输入数据时发现编辑视图中有错误的数 据,那么不必关闭对话框,就可以在编辑视图中进行修改。
分别按例 1 和例 2,对 CRegisterView 类和 CRegisterDialog 类进行修改。
例 1: CRegisterView 类的部分代码 CRegisterView::CRegisterView() // 构造函数初始化 {
// TODO: add construction code here m_pRegisterDlg=NULL; //指针初始化为 NULL }
void CModelessDialog::PostNcDestroy {
void CRegisterDialog::OnCancel() {
((CRegisterView *)GetParent())->pRegisterDlg = NULL; DestroyWindow(); //删除对话框 }
注:非模态对话框的显示问题:
新弹出的非模态子对话框总是显示在主界面的上面。即使当失去焦点时也会 显示在最前,使用很不方便。使用 SetWindowPos 这个函数不能完全解决问题,因 为 SetWindowPos 这个函数一旦设置了其中一个子对话框是 Topmost 那么该子对话 框的父对话框也自动改为 TopMost 属性。所以单单设置主对话框为 TopMost 是不 可以解决问题的。
//GetDesktopwindow()不能少,也不能填 NULL。
pModelessDlg ->ShowWindow(SW_SHOWNA); //想获得焦点就 SW_SHOWNA,否则 SW_SHOW 也可
需要注意的是,当销毁 modelessDlg 时,需要清空 modelessDlg 的指针 pModeless , 为 此 需 要 在 创 建 modeless 时 给 modelessDlg 类 添 加 一 个 变 量 m_pMainDlg 保存 mainDlg 的指针,在 OnOK 函数中添加如下代码:
请读者按下面几步操作:
1. 在登录数据对话框模板的属性对话框的 More Styles 页中选择 Visible 项。
2. 在 RegisterView.h 头文件的 CRegisterView 类的定义中加入
public:
CRegisterDialog* m_pRegisterDlg;
3. 在 RegisterView.h 头文件的头部加入对 CRegisterDialog 类的声明
编译并运行 Register,现在登录数据对话框已经变成一个非模态对话框了
void CRegisterDialog::PostNcDestroy() //在非模态对话框类中重写该虚函数 {
delete this; //删除对话框对象 }
// 如果有 OnOK 函数则需要重写,注意不要调用基类的 OnOK 函数
取消此特性的方法如下:
(1)将 Dialog 的属性“System Modal”(系统模块)设置为 True。
(2)创建代码,将非模态对话框的父窗口设置为桌面:
CDialog* pModelessDlg = new CDialog(NULL);
pModelessDlg->Create(IDD_MODELESSDIALOG,GetDesktopWindow());
5.4 非模态对话框
5.4.1 非模态对话框的特点
与模态对话框不同,非模态对话框不垄断用户的输入,用户打开非模态对话 框后,仍然可以与其它界面进行交互。
非模态对话框的设计与模态对话框基本类似,也包括设计对话框模板和设计 CDialog 类的派生类两部分。但是,在对话框的创建和删除过程中,非模态对话 框与模态对话框相比有下列不同之处:
delete this; //删除对象本身 }
这样,在删除屏幕上的对话框后,对话框对象将被自动删除。拥有者对象就 不必显式的调用 delete 来删除对话Байду номын сангаас对象了。
(5)必须有一个标志表明非模态对话框是否是打开的。这样做的原因是用户有 可能在打开一个模态对话框的情况下,又一次选择打开命令。程序根据标志来决 定是打开一个新的对话框,还是仅仅把原来打开的对话框激活。通常可以用拥有 者窗口中的指向对话框对象的指针作为这种标志,当对话框关闭时,给该指针赋 NULL 值,以表明对话框对象已不存在了。
4. 在 RegisterDialog.cpp 文件的头部的#include 语句区的末尾添加下面两行
#include "RegisterDoc.h"
#include "RegisterView.h"
5. 利用 ClassWizard 为 CRegisterDialog 类加入 OnCancel 和 PostNcDestroy 成 员函数。加入的方法是进入 ClassWizard 后选择 Message Maps 页,并在 Class name 栏中选择 CRegisterDialog。然后,在 Object IDs 栏中选择 IDCANCEL 后,在 Messages 栏中双击 BN_CLICKED,这就创建了 OnCancel。要创建 PostNcDestroy, 先 在 Object IDs 栏 中 选 择 CRegisterDialog , 再 在 Messages 栏 中 双 击 PostNcDestroy 即可。
class CRegisterDialog;
加入该行的原因是在 CRegisterView 类中有一个 CRegisterDialog 类型的指针, 因此必须保证 CRegisterDialog 类的声明出现在 CRegisterView 之前,否则编译 时 将 会 出 错 。 解 决 这 个 问 题 有 两 种 办 法 , 一 种 办 法 是 保 证 在 #include “RegisterView.h”语句之前有#include “RegisterDialog.h”语句,这种办法 造成了一种依赖关系,增加了编译负担,不是很好;另一种办法是在
(1)非模态对话框的模板必须具有 Visible 风格,否则对话框将不可见,而模 态对话框则无需设置该项风格。更保险的办法是调用 CWnd::ShowWindow(SW_SHOW) 来显示对话框,而不管对话框是否具有 Visible 风格。
(2)非模态对话框对象是用 new 操作符在堆中动态创建的,而不是以成员变量 的形式嵌入到别的对象中或以局部变量的形式构建在堆栈上。通常应在对话框的 拥有者窗口类内声明一个指向对话框类的指针成员变量,通过该指针可访问对话 框对象。
(4)必须调用 CWnd::DestroyWindow 而不是 CDialog::EndDialog 来关闭非模 态对话框。调用 CWnd::DestroyWindow 是直接删除窗口的一般方法。由于缺省的 CDialog::OnOK 和 CDialog::OnCancel 函数均调用 EndDialog,故程序员必须编写 自己的 OnOK 和 OnCancel 函数并且在函数中调用 DestroyWindow 来关闭对话框。
调用 CDialog::Create 来显示对话框,该函数的声明为
BOOL Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );
参数 nIDTemplate 是对话框模板的 ID。pParentWnd 指定了对话框的父窗口或 拥有者。以 CDialog 创建的非模态对话框调用 Create 函数时即使你指定父窗口为 NULL,实际的父窗口也不是 NULL,而是你程序的主窗口,所以你指定 NULL 和指
CRegisterView 类的声明之前加上一个对 CRegisterDialog 的声明来暂时“蒙蔽” 编译器,这样在有#include “RegisterView.h”语句的模块中,除非要用到 CRegisterDialog 类,否则不用加入#include “RegisterDialog.h”语句。
5.4.2 窗口对象的自动清除
一个 MFC 窗口对象包括两方面的内容:一是窗口对象封装的窗口,即存放在 m_hWnd 成员中的 HWND(窗口句柄),二是窗口对象本身是一个 C++对象。要删除 一个 MFC 窗口对象,应该先删除窗口对象封装的窗口,然后删除窗口对象本身。
m_pMainDlg->m_pDspStateDlg = NULL;
(获取对话框 Dialog 的背景色用 SDK 函数 GetSysColor(COLOR_BTNFACE))
上面的方法有个特性:在任务栏会显示非模态对话框的图标。如果不想在任 务栏显示该图标是不是可以尝试如下方法:可以创建一个隐藏的对话框,然后在 这个对话框上面创建主对话框,把这个主对话框设为 TopMost 就可以了?
定 this 是一样的效果,而指定 GetDesktopWindow()则把非模态窗口的父窗口指 定为桌面了,这时与你程序的主窗口不再有关系。
void CRegisterView::OnEditRegister() // 创建非模态对话框 {
// TODO: Add your command handler code here if(m_pRegisterDlg)
CRegisterView::OnEditRegister 函数判断登录数据对话框是否已打开,若是, 就激活对话框,否则,就创建该对话框。该函数中主要调用了下列函数:
调用 CWnd::SetActiveWindow 激活对话框,该函数的声明为:
CWnd* SetActiveWindow( );
该函数使本窗口成为活动窗口,并返回原来活动的窗口。
m_pRegisterDlg->SetActiveWindow(); //激活对话框 else {
//创建非模态对话框 m_pRegisterDlg=new CRegisterDialog(this); m_pRegisterDlg->Create(IDD_REGISTER,this); } }
例 2: CRegisterDialog 的部分代码
(3) 通 过 调 用 CDialog::Create 函 数 来 启 动 对 话 框 , 而 不 是 CDialog::DoModal,这是模态对话框的关键所在。由于 Create 函数不会启动新的 消息循环,对话框与应用程序共用同一个消息循环,这样对话框就不会垄断用户 的输入。Create 在显示了对话框后就立即返回,而 DoModal 是在对话框被关闭后 才返回的。众所周知,在 MFC 程序中,窗口对象的生存期应长于对应的窗口,也 就是说,不能在未关闭屏幕上窗口的情况下先把对应的窗口对象删除掉。由于在 Create 返回后,不能确定对话框是否已关闭,这样也就无法确定对话框对象的生 存期,因此只好在堆中构建对话框对象,而不能以局部变量的形式来构建之。
当用户在登录数据对话框中点击“取消”按钮后,CRegisterDialog::OnCancel 将 被 调 用 , 在 该 函 数 中 调 用 CWnd::DestroyWindow 来 关 闭 对 话 框 , 并 且 将 CRegisterView 的成员 m_pRegisterDlg 置为 NULL 以表明对话框被关闭了。调用 DestroyWindow 导致了对 CRegisterDialog::PostNcDestroy 的调用,在该函数中 用 delete 操作符删除了 CRegisterDialog 对象本身。
提示:在 C++编程中,判断一个位于堆中的对象是否存在的常用方法是判断指向 该对象的指针是否为空。这种机制要求程序员将指向该对象的指针初始化为 NULL 值,在创建对象时将返回的地址赋给该指针,而在删除对象时将该指针置成 NULL 值。
根据上面的分析,我们很容易把 Register 程序中的登录数据对话框改成非模 态对话框。这样做的好处在于如果用户在输入数据时发现编辑视图中有错误的数 据,那么不必关闭对话框,就可以在编辑视图中进行修改。
分别按例 1 和例 2,对 CRegisterView 类和 CRegisterDialog 类进行修改。
例 1: CRegisterView 类的部分代码 CRegisterView::CRegisterView() // 构造函数初始化 {
// TODO: add construction code here m_pRegisterDlg=NULL; //指针初始化为 NULL }