Visual C++2013从入门到精通(视频教学版)
上QQ阅读APP看书,第一时间看更新

3.7 模态对话框和非模态对话框

所谓模态对话框就是运行时,当它获得焦点时,将垄断用户的输入,在关闭本对话框之前,用户无法对本程序的其他部分进行操作。而非模态对话框类似于Word里的查找替换,就在应用程序打开非模态对话框的同时还可以切换到其他窗口进行操作。这是功能上的区别,在编程实现的时候也是不一样的。

模态对话框使用CDialog::DoModal函数来创建,DoModal会启动一个模态对话框自己的消息循环,这也是模态对话框要关闭后才能使用程序其他窗口的原因。DoModal函数在对话框关闭后才返回。

非模态对话框使用CDialog::Create函数实现,由于Create函数不会启动新的消息循环,对话框与应用程序共用一个消息循环,因此就不会独占用户输入,下面用2个实例来说明模态对话框和非模态对话框的创建过程。

【例3.8】 创建一个模态对话框

(1)打开Visual C++ 2013,新建一个对话框工程,工程名是Test。

(2)切换到资源视图,打开对话框并删除上面所有控件,然后添加一个按钮,标题是“显示模态对话框”。接着,对资源视图中的Dialog右击,出现右击菜单,在上面选择“插入Dialog”,如图3-39所示。

图3-39

此时系统会新建一个对话框,左边可以看到新建对话框的界面,右边Dialog下多出来一个名为IDD_DIALOG1的ID了。打开IDD_DIALOG1的属性视图,然后在属性视图里,把ID改为IDD_MODEL,把Caption属性改为“模态对话框”。并把新建的对话框上的控件都删除,然后在对话框空白处右击,在右击菜单中选择“添加类”,出现“MFC添加类向导”对话框,如图3-40所示。

图3-40

在上面选择基类为“CDialog”,基类也可以选择“CDialogEx”, CDialogEx只是比CDialog扩展了一些功能。接着,输入类名为CDlgModel,类名可以自己决定,但最好以C开头,表示它是个类。最后单击“完成”按钮,此时系统自动打开DlgModel.h,这是我们刚才新建对话框类的头文件。

(3)切换到资源视图,打开ID为IDD_TEST_DIALOG的对话框,然后对上面的“显示模态对话框”按钮双击来添加按钮消息处理函数,添加代码如下:

        void CTestDlg::OnBnClickedButton1()
        {
         // TODO:  在此添加控件通知处理程序代码
         CDlgModel dlg;
         dlg.DoModal(); //创建并显示模态对话框
        }

然后在TestDlg.cpp的开头添加包含文件:

        #include "DlgModel.h"

(4)保持工程并运行,运行结果如图3-41所示。

此时在Test对话框的关闭按钮或其他任何区域单击,发现它的功能都失效了,一直要等到模态对话框关闭以后,Test对话框才能恢复正常使用。

【例3.9】 创建一个非模态对话框

(1)打开Visual C++ 2013,新建一个对话框工程,工程名是Test。

(2)切换到资源视图,打开对话框并删除上面所有控件,然后添加一个按钮,标题是“显示非模态对话框”。接着,对资源视图中的Dialog右击,出现右击菜单,在上面选择“插入Dialog”,此时将插入一个新的对话框。把新对话框上的按钮都删除,然后修改对话框的标题为“非模态对话框”,再在对话框上右击,然后选择“添加类”,出现类向导对话框,如图3-42所示。

图3-41

图3-42

在向导对话框上输入类名为CDlgModeless,并选择基类为CDialog,然后单击“完成”。

(3)切换到解决方案管理器,打开文件TestDlg.cpp,然后在开头添加包含文件:

        #include "DlgModeless.h"

并在该文件中定义一个全局变量:

        CDlgModeless *g_dlgModeless=NULL; //定义非模态对话框指针

(4)切换到资源视图,打开ID为IDD_TEST_DIALOG的对话框,在上面对标题为“显示非模态对话框”的按钮双击,添加按钮消息处理函数,添加代码如下:

        void CTestDlg::OnBnClickedButton1()
        {
         // TODO:  在此添加控件通知处理程序代码
         if (! g_dlgModeless)//如果非模态对话框对象还没创建则新建
         {
            g_dlgModeless = new CDlgModeless;
            g_dlgModeless->Create(CDlgModeless::IDD, this); //创建非模态对话框
         }
         g_dlgModeless->ShowWindow(SW_SHOW); //显示非模态对话框
         g_dlgModeless->SetActiveWindow();
        }

非模态对话框的创建和显示完成了,在上面的代码中我们新生成了一个对话框对象(new CDlgModeless),而且在函数OnBnClickedButton1执行完毕时并没有销毁该对象。如果在函数OnBnClickedButton1的末尾销毁该对象(对象被销毁时窗口同时被销毁),而此时对话框还在显示,那么就会出现错误。但不销毁是不行的,熟悉C++的朋友都知道,new和delete必须对应起来使用,否则会造成内存的泄漏。

这就引出了一个问题:什么时候销毁该对象。通常使用的方法是在对话框退出时销毁自己。在非模态对话框中重载OnOK与OnCancel,并在函数中调用父类的同名函数,然后调用DestroyWindow()强制销毁窗口,接着在对话框中响应WM_DESTROY消息,在消息处理函数中调用delete this;来强行删除自身对象。

(5)切换到类视图,如果类视图没有打开,可以通过菜单“视图”|“类视图”来打开。在类视图上,右击CDlgModeless,在右击菜单上选择属性,打开类CDlgModeless的属性视图,在属性视图上,单击“重写”按钮,如图3-43所示。

“重写”页下的函数都是可以用来重载的,在上面我们找到OnOK,在右边单击下拉箭头来添加OnOK函数,如图3-44所示。

图3-43

图3-44

同样的方法,添加OnCancel()重载函数。然后在OnOK和OnCancel函数添加代码如下:

        void CDlgModeless::OnOK()
        {
         // TODO:  在此添加专用代码和/或调用基类
         CDialog::OnOK();
         DestroyWindow(); //销毁窗口,会发出WM_DESTROY消息
        }
        void CDlgModeless::OnCancel()
        {
         // TODO:  在此添加专用代码和/或调用基类
         CDialog::OnCancel();
         DestroyWindow(); //销毁窗口,会发出WM_DESTROY消息
        }

(6)响应对话框的WM_DESTROY消息。切换类视图,单击CDlgModeless,然后在其属性窗口中,选择消息页面,然后在消息列表中添加WM_DESTROY的响应函数OnDestroy,如图345所示。

然后在OnDestroy中添加代码如下:

        void CDlgModeless::OnDestroy()
        {
        CDialog::OnDestroy();
        // TODO:  在此处添加消息处理程序代码
        delete this;
        g_dlgModeless = NULL;
       }

在这个函数中,终于出现了delete了,这样不担心内存泄露了。在DlgModeless.cpp的开头添加全局变量的引用:extern CDlgModeless *g_dlgModeless;。

(7)保存工程并运行。运行结果如图3-46所示。

图3-45

图3-46

在非模态对话框显示出来后,我们照样可以单击Test对话框的系统菜单。