对话框就是一个窗口,可以接收消息、移动、关闭以及在它的客户区绘图。
所有的控件也是一个个窗口。控件通常作为对话框的子窗口而创建的,也出现在视类窗口、工具栏和状态条中。以后会陆陆续续介绍相应的控件。
对话框有两种类型:模态对话框和非模态对话框。
模态对话框:模态对话框显示的时候,程序会暂停执行。直到关闭这个对话框后,才能继续执行程序。
非模态对话框:对话框显示时候,可以允许转而执行程序中的其他任务,而不用关闭这个对话框。
建立一个单文档工程,工程名为:Mybole。
运行,然后单击菜单栏帮助,就会出现一个帮助对话框。
创建自己的对话框:通过插入一个对话框资源来完成。
Insert->Resource->选中Dialog->New
在ResourceView选项Dialog项下就会出现标识为IDD_DIALOG1名称的对话框资源,同时在资源编辑窗口中打开了这个新对话框资源。如图所示
也可以单击资源工具栏上面->New Dialog按钮来新建一个对话框资源。
可以看到IDD_DIALOG1对话框有两个按钮:OK和Cancel。
他们ID分别为IDOK和IDCANCEL,VC++已经为这两个按钮提供了默认的消息响应函数OnOK和OnCancel。他们功能都是关闭对话框,但是单击不同按钮,用来确定或者取消当前操作。
打开IDD_DIALOG1对话框属性,将Caption属性设置为“测试”。
在MFC中,对资源的操作是通过一个与资源相关类来完成,对话框资源也有一个相应的基类:CDialog。
因此需要创建一个类与新建的对话框资源相关联:
View->ClassWizard,就会弹出一个对话框,提示IDD_DIALOG1对话框是一个新资源,需要为它创建一个相关联的类或者用已有的一个类与它相关联。这里选择前者,单击OK。就成功创建一个与IDD_DIALOG1对话框相关联的类。
单击OK后就会出现下图。
在Name编辑框中输入新类名称:CTestDlg,File Name自动出现文件名:TestDlg.cpp。
单击Change,可以改变新类的头文件名和源文件名。这里不做改变。
单击OK后,在ClassView选项中多了这个新类。并且CTestDlg这个类有两个成员函数:一个就是它的构造函数;一个是DoDateExchange函数。
构造函数:
CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTestDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CTestDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
CTestDlg构造函数先调用其基类:CDialog类的构造函数,传递两个参数:一个是CTestDlg类的IDD成员,即这个对话框资源ID;一个是父窗口指针。
DoDateExchange函数:
void CTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTestDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
完成对话框数据的交换和检验。
这样CTestDlg与IDD_DIALOG1对话框资源相关联了。接下来就是显示这个对话框窗口。
首先增加一个菜单项,当单击这个菜单项就显示该对话框:
在Mybole程序的帮助菜单项后面添加一个名为对话框的菜单项,单击菜单项属性,将Caption改为“对话框”,将Pop-up勾选去掉,ID为IDM_DIALOG。
接着利用ClassWizard为该菜单项添加COMMAND命令消息响应函数。让视类捕获命令响应,同时在该消息响应函数中实现对该对话框窗口的显示。
创建模态对话框,需要调用CDialog类的成员函数:DoModal,该函数的功能就是创建并且显示一个模态对话框。其返回值可以作为CDialog类另一个成员函数:EndDialog函数的参数,该函数的功能就是关闭模态对话框。
void CMyboleView::OnDialog()
{
// TODO: Add your command handler code here
CTestDlg dlg;
dlg.DoModal();
}
首先定义一个对话框对象,然后利用该对象调用DoModal函数。
同时视类源文件应该包含CTestDlg类的头文件。
#include “TestDlg.h”
运行,单击对话框菜单项,就出现下图。
非模态对话框的创建,需要用到CDialog类的Create成员函数。
BOOL Create( LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL );
BOOL Create( UINT nIDTemplate, CWnd* pParentWnd = NULL );
Create函数第一个参数可以是对话框模板的名称;或者是对话框资源的ID。第二个参数指定对话框的父窗口,若为NULL,对于本例来说就是框架窗口。
void CMyboleView::OnDialog()
{
// TODO: Add your command handler code here
CTestDlg dlg;
//dlg.DoModal();
dlg.Create(IDD_DIALOG1,this);
dlg.ShowWindow(SW_SHOW);
}
this表示对话框的父窗口就是视类窗口。
由于Create函数不具有显示对话框的功能,所以要调用ShowWindow函数。
运行,发现对话框并没有显示出来:是由于dlg是一个局部变量。
调用DoModal函数之所以会显示出来,是因为程序执行到该函数会停下来,dlg变量并没有销毁。
而调用ShowWindow,程序继续运行,dlg变量的生命周期在出了该消息响应函数后就结束了,所以对话框就消失了。
两种解决方法:
1、将该变量变成视类的成员变量。
2、将它定义为指针,在堆上分配内存。
void CMyboleView::OnDialog()
{
// TODO: Add your command handler code here
//CTestDlg dlg;
//dlg.DoModal();
CTestDlg *pDlg=new CTestDlg;
pDlg->Create(IDD_DIALOG1,this);
pDlg->ShowWindow(SW_SHOW);
}
但是还有问题:pDlg变量仍然是一个局部变量,生命周期结束后,它保存的内存地址就丢失了。那么在程序中就无法引用那一块内存了。
解决方法:
1、将pDlg变量定义为视类的成员变量,同时在视类的析构函数中用Delete函数来释放这个指针变量所指向的内存。
2、在CTestDlg类中重载PostNcDestroy虚函数,释放this指针指向的内存。
鼠标单击视类右键->Add Virtue Function
void CTestDlg::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
delete this;
CDialog::PostNcDestroy();
}
单击OK按钮时,模态对话框对象被销毁;而非模态对话框对象没有销毁,而是隐藏起来了。
因此,对于非模态对话框来说,如果有一个IDOK的按钮,就必须重写基类的OnOK虚函数,在重写的OnOK函数中调用DestroyWindows函数,完成对话框的销毁工作。