MFC下的模态与非模态对话框

以前学VC的时候觉得模态与非模态很简单,两个的创建方式不同,一个是Create一个是DoModel,然后如果是模态的就很霸道,不让你去点后面的父窗口,如果是非模态就可以点击后面的父窗口,但是在实际的应用发现一点很需要注意的地方:

 

我们如果便写过window SDK,可以知道,其实窗口的创建不关事父窗口还是子窗口,不管是模态还是非模态,其最基本的的调用的都是CreateWindow或者CreateWindowEx,但是就是这两个函数创建的窗口使用起来就很大不同,我们一点一点的说。

 

首先,一般来说我们都有一个主窗口,这个很简单,在MFC中,这个主窗口可能是一个基于Dialog的,也可能是一个基于View的,这个都比较简单。

 

好,现在我们要使用模态对话框, 非模态的对话框是独立的,就是说和主程序可以同时交换数据,而模态的对话框则只能和自己交换。换句话说,模态对话框很霸道,你开了它就无法在同一个应用程序中的其他地方干活了,比如那个经常用的openfile dialog。非模态对话框就很随和,开了它你还可以爱干嘛干嘛。

 

我们创建的代码是: 
  CMyDlg   dlg;  
  dlg.Domdal();  

这样你的模态对话框就霸道的出来了,注意这里的CMyDlg 可以是你的成员变量,在这个对话框不关掉之前,你是不能操作其他的东东的,我们可以猜测一下,这个函数干了什么,首先create一个window,然后不让你点击其他的区域。

好,现在我们创建一个非模态的对话框,代码如下;

CMyDlg*   pMainWnd   =   new   CMyDlg;  
  pMainWnd->Create();  

有人或许会问,我能不能把CMyDlg声明为一个变量,而不是一个指针,我试过,同样可以创建,但是发现了一个问题,我的这个对话框就没有独立性了,它就变成了一个子窗口,从属与父窗口,这个从属关系,要比一般的对话框要紧密的多,就相当于一个子窗口控件,可能说的还不是很清楚,子窗口+父窗口=1个窗口,对画框+父窗口=2个窗口,虽然两个窗口关系很紧密(如何紧密我们后面讲),首先,什么是子窗口,子窗口创建在父窗口上,就完全成为了父窗口的一部分了,父窗口说,我的这块地就给你,我不管了,以后你想怎么干久怎么干,干好了跟我说一下就可以了(发送消息),什么是对话框呢,这里有两块地,一块是我的,一块是你的,两个人都可以想怎么干就怎么干,但是我比较狠,你在一定程度上还得听我的,这个就是对话框,其实就是一块地与两块地的区别,有人可能会说,这个区别有什么影响,我们来说一下影响?最常见的是窗口刷新的时候,如果是子窗口的情况,当父窗口要刷新子窗口的时候,什么事情都不做,对子窗口说,起来了,把你负责的那一块刷一下;如果是对话框,父窗口首先把自己的这一块刷了一下,然后就对对话框说,既然我是老大,你看我都刷新了,是不是你也应该刷新了呢?然后对话框就闷闷不乐的刷新他的那一块。

 

这点区别有什么用呢?首先,当我们在子窗口中贴一张透明的图的时候,在擦出背景消息里面直接返回的时候,会发现我靠父窗口啥也没有干,显示的是桌面;但是如果是对话框,对话框里面贴的是一张透明的图的时候,会发现我不干,你也还是干了,看到的就不是桌面,而是父窗口对应的区域。

 

这个问题困扰我很久,感觉一个是指针一个是变量,创建的窗口就完全不同了,郁闷。

 

下面,我们来说一下模态与非模态的区别,模态很霸道,他把焦点始终强制转移到他自己的身上,当你点击其他区域的时候,会发出警告,提示你必须这个对话框销毁你才能够做别的事情。如果是非模态,如果点击对话框区域,对话框就获得焦点,如果不是,那么焦点就给了其他区域,有人或许会问,这个时候这个对话框会不会被窗口掩盖在下面,答案是不会,虽然不是亲生的父子关系,没有血融于水,但是怎么说也是父子关系,除非是平级的窗口才会这样,当一个窗口获得焦点的时候其他的窗口都在他的下面。

 

还有一个区别在于内存分配,new在堆中,另一在栈中,一般说两者一样,但如果你在对话框中开了很大的空间做成员,第二种就会有问题了,因为栈空间是受限的。但据说解决栈溢出用static申明,在全局数据区分配内存就可以了(我没试过)!模态消息不进message   loop;非模态消息进入message   loop。

如何使用vc的非模态对话框
1、非模态对话框的模板必须具有Visible风格,否则对话框将不可见,而模态对话框则无需设置该项风格。更保险的办法是调用CWnd::ShowWindow(SW_SHOW)来显示对话框,而不管对话框是否具有Visible风格。
2、非模态对话框对象是用new操作符在堆中动态创建的,而不是以成员变量的形式嵌入到别的对象中或以局部变量的形式构建在堆栈上。 通常应在对话框的拥有者窗口类内声明一个指向对话框类的指针成员变量,通过该指针可访问对话框对象。
3、*通过调用CDialog::Create函数来启动对话框,而不是CDialog::DoModal,这是模态对话框的关键所在。由于Create函数不会启动新的消息循环,对话框与应用程序共用同一个消息循环,这样对话框就不会垄断用户的输入。Create在显示了对话框后就立即返回,而DoModal是在对话框被关闭后才返回的。众所周知,在MFC程序中,窗口对象的生存期应长于对应的窗口,也就是说,不能在未关闭屏幕上窗口的情况下先把对应的窗口对象删除掉。由于在Create返回后,不能确定对话框是否已关闭,这样也就无法确定对话框对象的生存期,因此只好在堆中构建对话框对象,而不能以局部变量的形式来构建之。

4、代码示例:
对话框资源ID为:IDD_MYDIALOG
对话框的类定义为:CMyDialog
CMyView的成员变量为:CMyDialog* m_myDlg

CMyView::OnOpenDialogButton()
{
  CMainFrame* pWnd=(CMainFrame*)AfxGetMainWnd();
  ASSERT_VALID(pWnd);    //定义父窗口指针pWnd
  m_myDlg=new CMyDialog(pWnd);    //堆分配非模态对话框内存空间,   //CMyDialog(CWnd* pParent=NULL)为构造函数
  m_myDlg->Create(IDD_MYDIALOG,pWnd);
  m_myDlg->ShowWindow(SW_SHOW);
  //可用this指针代替pWnd指针,则省略头两行
}

5、必须有一个标志表明非模态对话框是否是打开的。这样做的原因是用户有可能在打开一个模态对话框的情况下,又一次选择打开命令。程序根据标志来决定是打开一个新的对话框,还是仅仅把原来打开的对话框激活。通常可以用拥有者窗口中的指向对话框对象的指针作为这种标志,当对话框关闭时,给该指针赋NULL值,以表明对话框对象已不存在了。
[注]:在C++编程中,判断一个位于堆中的对象是否存在的常用方法是判断指向该对象的指针是否为空。这种机制要求程序员将指向该对象的指针初始化为NULL值,在创建对象时将返回的地址赋给该指针,而在删除对象时将该指针置成NULL值。

6、必须调用CWnd::DestroyWindow而不是CDialog::EndDialog来关闭非模态对话框。调用CWnd::DestroyWindow是直接删除窗口的一般方法。由于缺省的CDialog::OnOK和CDialog::OnCancel函数均调用EndDialog,故程序员必须编写自己的OnOK和OnCancel函数并且在函数中调用DestroyWindow来关闭对话框。

 


7、因为是用new操作符构建非模态对话框对象,因此必须在对话框关闭后,用delete操作符删除对话框对象。在屏幕上一个窗口被删除后,框架会调用CWnd::PostNcDestroy,这是一个虚拟函数,程序可以在该函数中完成删除窗口对象的工作,具体代码如下
void CModelessDialog::PostNcDestroy
{
delete this; //删除对象本身
}
这样,在删除屏幕上的对话框后,对话框对象将被自动删除。拥有者对象就不必显式的调用delete来删除对话框对象了。也可以通过由的DestroyWindow()引起的WM_DESTROY消息处理函数OnDestroy()种实现delete this操作。

void CModelessDlg::OnDestroy()
{
 CDialog::OnDestroy(); 
 delete this; 
}

你可能感兴趣的:(编程,框架,null,delete,mfc,dialog)