第五章 对话框.docx
《第五章 对话框.docx》由会员分享,可在线阅读,更多相关《第五章 对话框.docx(38页珍藏版)》请在冰豆网上搜索。
![第五章 对话框.docx](https://file1.bdocx.com/fileroot1/2023-5/26/70cc6e8e-e397-4c1d-a8d2-887044eb523c/70cc6e8e-e397-4c1d-a8d2-887044eb523c1.gif)
第五章对话框
第五章对话框
5.1使用资源编辑器编辑对话框
在Windows开发中弹出对话框是一种常用的输入/输出手段,同时编辑好的对话框可以保存在资源文件中。
VisualC++提供了对话框编辑工具,利用编辑工具可以方便的添加各种控件到对话框中,而且利用ClassWizard可以方便的生成新的对话框类和映射消息。
首先资源列表中按下右键,可以在弹出菜单中选择“插入对话框”,如图1。
图1
然后再打开该对话框进行编辑,你会在屏幕上看到一个控件板,如图2。
图2
你可以将所需要添加的控件拖到对话框上,或是先选中后再在对话框上用鼠标画出所占的区域。
接下来我们在对话框上产生一个输入框,和一个用于显示图标的图片框。
之后我们使用鼠标右键单击产生的控件并选择其属性,如图3。
图3
我们可以在属性对话框中编辑控件的属性同时也需要指定控件ID,如图4,如果在选择对话框本身的属性那么你可以选择对话框的一些属性,包括字体,外观,是否有系统菜单等等。
图4
最后我们编辑图片控件的属性,如图5,我们设置控件的属性为显示图标并指明一个图标ID。
图5
接下来我们添加一些其他的控件,最后的效果如图6。
图6
按下Ctrl-T可以测试该对话框。
此外在对话框中还有一个有用的特性,就是可以利用Tab键让输入焦点在各个控件间移动,要达到这一点首先需要为控件设置在Tab键按下时可以接受焦点移动的属性TabStop,如果某一个控件不打算利用这一特性,你需要清除这一属性。
然后从菜单“Layout”选择TabOrder来确定焦点移动顺序,如图7。
图7
使用鼠标依此点击控件就可以重新规定焦点移动次序。
最后按下Ctrl-T进行测试。
最后我们需要为对话框产生新的类,ClassWizard可以替我们完成大部分的工作,我们只需要填写几个参数就可以了。
在编辑好的对话框上双击,然后系统回询问是否添加新的对话框,选择是并在接下来的对话框中输入类名就可以了。
ClassWizard会为你产生所需要的头文件和CPP文件。
然后在需要使用的地方包含相应的头文件,对于有模式对话框使用DoModal()产生,对于无模式对话框使用Create()产生。
相关代码如下:
voidCMy51_s1View:
:
OnCreateDlg()
{//产生无模式对话框
CTestDlg*dlg=newCTestDlg;
dlg->Create(IDD_TEST_DLG);
dlg->ShowWindow(SW_SHOW);
}
voidCMy51_s1View:
:
OnDoModal()
{//产生有模式对话框
CTestDlgdlg;
intiRet=dlg.DoModal();
TRACE("dlgreturn%d\n",iRet);
}
下载例子。
如果你在调试这个程序时你会发现程序在退出后会有内存泄漏,这是因为我没有释放无模式对话框所使用的内存,这一问题会在以后的章节5.3创建无模式对话框中专门讲述。
关于在使用对话框时Enter键和Escape键的处理:
在使用对话框是你会发现当你按下Enter键或Escape键都会退出对话框,这是因为Enter键会引起CDialog:
:
OnOK()的调用,而Escape键会引起CDialog:
:
OnCancel()的调用。
而这两个调用都会引起对话框的退出。
在MFC中这两个成员函数都是虚拟函数,所以我们需要进行重载,如果我们不希望退出对话框那么我们可以在函数中什么都不做,如果需要进行检查则可以添加检查代码,然后调用父类的OnOK()或OnCancel()。
相关代码如下:
voidCTestDlg:
:
OnOK()
{
AfxMessageBox("你选择确定");
CDialog:
:
OnOK();
}
voidCTestDlg:
:
OnCancel()
{
AfxMessageBox("你选择取消");
CDialog:
:
OnCancel();
}
5.2创建有模式对话框
使用有模式对话框时在对话框弹出后调用函数不会立即返回,而是等到对话框销毁后才会返回(请注意在对话框弹出后其他窗口的消息依然会被传递)。
所以在使用对话框时其他窗口都不能接收用户输入。
创建有模式对话框的方法是调用CDialog:
:
DoModal()。
下面的代码演示了这种用法:
CYourView:
:
OnOpenDlg()
{
CYourDlgdlg;
intiRet=dlg.DoModal();
}
CDialog:
:
DoModal()的返回值为IDOK,IDCANCEL。
表明操作者在对话框上选择“确认”或是“取消”。
由于在对话框销毁前DoModal不会返回,所以可以使用局部变量来引用对象。
在退出函数体后对象同时也会被销毁。
而对于无模式对话框则不能这样使用,下节5.3创建无模式对话框中会详细讲解。
你需要根据DoModal()的返回值来决定你下一步的动作,而得到返回值也是使用有模式对话框的一个很大原因。
使用有模式对话框需要注意一些问题,比如说不要在一些反复出现的事件处理过程中生成有模式对话框,比如说在定时器中产生有模式对话框,因为在上一个对话框还未退出时,定时器消息又会引起下一个对话框的弹出。
同样的在你的对话框类中为了向调用者返回不同的值可以调用CDialog:
:
OnOK()或是CDialog:
:
OnCancel()以返回IDOK或IDCANCEL,如果你希望返回其他的值,你需要调用
CDialog:
:
EndDialog(intnResult);其中nResult会作为DoModal()调用的返回值。
下面的代码演示了如何使用自己的函数来退出对话框:
下载例子
voidCMy52_s1View:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{//创建对话框并得到返回值
CView:
:
OnLButtonDown(nFlags,point);
CTestDlgdlg;
intiRet=dlg.DoModal();
CStringszOut;
szOut.Format("returnvalue%d",iRet);
AfxMessageBox(szOut);
}
//重载OnOK,OnCancel
voidCTestDlg:
:
OnOK()
{//什么也不做
}
voidCTestDlg:
:
OnCancel()
{//什么也不做
}
//在对话框中对三个按钮消息进行映射
voidCTestDlg:
:
OnExit1()
{
CDialog:
:
OnOK();
}
voidCTestDlg:
:
OnExit2()
{
CDialog:
:
OnCancel();
}
voidCTestDlg:
:
OnExit3()
{
CDialog:
:
EndDialog(0XFF);
}
由于重载了OnOK和OnCancel所以在对话框中按下Enter键或Escape键时都不会退出,只有按下三个按钮中的其中一个才会返回。
此外在对话框被生成是会自动调用BOOLCDialog:
:
OnInitDialog(),你如果需要在对话框显示前对其中的控件进行初始化,你需要重载这个函数,并在其中填入相关的初始化代码。
利用ClassWizard可以方便的产生一些默认代码,首先打开ClassWizard,选择相应的对话框类,在右边的消息列表中选择WM_INITDIALOG并双击,如图,
ClassWizard会自动产生相关代码,代码如下:
BOOLCTestDlg:
:
OnInitDialog()
{
/*先调用父类的同名函数*/
CDialog:
:
OnInitDialog();
/*填写你的初始化代码*/
returnTRUE;
}
有关对对话框中控件进行初始化会在5.4在对话框中进行消息映射中进行更详细的讲解。
5.3创建无模式对话框
无模式对话框与有模式对话框不同的是在创建后其他窗口都可以继续接收用户输入,因此无模式对话框有些类似一个弹出窗口。
创建无模式对话框需要调用
BOOLCDialog:
:
Create(UINTnIDTemplate,CWnd*pParentWnd=NULL);之后还需要调用
BOOLCDialog:
:
ShowWindow(SW_SHOW);进行显示,否则无模式对话框将是不可见的。
相关代码如下:
voidCYourView:
:
OnOpenDlg(void)
{
/*假设IDD_TEST_DLG为已经定义的对话框资源的ID号*/
CTestDlg*dlg=newCTestDlg;
dlg->Create(IDD_TEST_DLG,NULL);
dlg->ShowWindows(SW_SHOW);
/*不要调用deletedlg;*/
}
在上面的代码中我们新生成了一个对话框对象,而且在退出函数时并没有销毁该对象。
因为如果此时销毁该对象(对象被销毁时窗口同时被销毁),而此时对话框还在显示就会出现错误。
那么这就提出了一个问题:
什么时候销毁该对象。
我时常使用的方法有两个:
∙在对话框退出时销毁自己:
在对话框中重载OnOK与OnCancel在函数中调用父类的同名函数,然后调用DestroyWindow()强制销毁窗口,在对话框中映射WM_DESTROY消息,在消息处理函数中调用deletethis;强行删除自身对象。
相关代码如下:
∙voidCTestDlg1:
:
OnOK()
∙{
∙CDialog:
:
OnOK();
∙DestroyWindow();
∙}
∙
∙voidCTestDlg1:
:
OnCancel()
∙{
∙CDialog:
:
OnCancel();
∙DestroyWindow();
∙}
∙
∙voidCTestDlg1:
:
OnDestroy()
∙{
∙CDialog:
:
OnDestroy();
∙AfxMessageBox("calldeletethis");
∙deletethis;
∙}
这种方法的要点是在窗口被销毁的时候,删除自身对象。
所以你可以在任何时候调用DestroyWindow()以达到彻底销毁自身对象的作用。
(DestroyWindow()的调用会引起OnDestroy()的调用)
∙通过向父亲窗口发送消息,要求其他窗口对其进行销毁:
首先需要定义一个消息用于进行通知,然后在对话框中映射WM_DESTROY消息,在消息处理函数中调用消息发送函数通知其他窗口。
在接收消息的窗口中利用ON_MESSAGE映射处理消息的函数,并在消息处理函数中删除对话框对象。
相关代码如下:
∙/*更改对话框的有关文件*/
∙CTestDlg2:
:
CTestDlg2(CWnd*pParent/*=NULL*/)
∙:
CDialog(CTestDlg2:
:
IDD,pParent)
∙{/*m_pParent为一成员变量,用于保存通知窗口的指针,
∙所以该指针不能是一个临时指针*/
∙ASSERT(pParent);
∙m_pParent=pParent;
∙//{{AFX_DATA_INIT(CTestDlg2)
∙//NOTE:
theClassWizardwilladdmemberinitializationhere
∙//}}AFX_DATA_INIT
∙}
∙voidCTestDlg2:
:
OnOK()
∙{
∙CDialog:
:
OnOK();
∙DestroyWindow();
∙}
∙
∙voidCTestDlg2:
:
OnCancel()
∙{
∙CDialog:
:
OnCancel();
∙DestroyWindow();
∙}
∙
∙voidCTestDlg2:
:
OnDestroy()
∙{
∙CDialog:
:
OnDestroy();
∙/*向其他窗口发送消息,将自身指针作为一个参数发送*/
∙m_pParent->PostMessage(WM_DELETE_DLG,(WPARAM)this);
∙}
∙
∙/*在消息接收窗口中添加消息映射*/
∙/*在头文件中添加函数定义*/
∙afx_msgLONGOnDelDlgMsg(WPARAMwP,LPARAMlP);
∙/*添加消息映射代码*/
∙ON_MESSAGE(WM_DELETE_DLG,OnDelDlgMsg)
∙END_MESSAGE_MAP()
∙/*实现消息处理函数*/
∙LONGCMy53_s1View:
:
OnDelDlgMsg(WPARAMwP,LPARAMlP)
∙{
∙delete(CTestDlg2*)wP;
∙return0;
∙}
∙/*创建对话框*/
∙voidCMy53_s1View:
:
OnTest2()
∙{
∙CTestDlg2*dlg=newCTestDlg2(this);
∙dlg->Create(IDD_TEST_DLG_2);
∙dlg->ShowWindow(SW_SHOW);
∙}
在这种方法中我们利用消息来进行通知,在Window系统中利用消息进行通知和传递数据的用法是很多的。
同样无模式对话框的另一个作用还可以用来在用户在对话框中的输入改变时可以及时的反映到其他窗口。
下面的代码演示了在对话框中输入一段文字,然后将其更新到视图的显示区域中,这同样也是利用了消息进行通知和数据传递。
/*在对话框中取出数据,并向其他窗口发送消息和数据,将数据指针作为一个参数发送*/
voidCTestDlg2:
:
OnCommBtn()
{
charszOut[30];
GetDlgItemText(IDC_OUT,szOut,30);
m_pParent->SendMessage(WM_DLG_NOTIFY,(WPARAM)szOut);
}
/*在消息接收窗口中*/
/*映射消息处理函数*/
ON_MESSAGE(WM_DLG_NOTIFY,OnDlgNotifyMsg)
/*在视图中绘制出字符串m_szOut*/
voidCMy53_s1View:
:
OnDraw(CDC*pDC)
{
CMy53_s1Doc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
//TODO:
adddrawcodefornativedatahere
pDC->TextOut(0,0,"DisplayString");
pDC->TextOut(0,20,m_szOut);
}
/*处理通知消息,保存信息并更新显示*/
LONGCMy53_s1View:
:
OnDlgNotifyMsg(WPARAMwP,LPARAMlP)
{
m_szOut=(char*)wP;
Invalidate();
return0;
}
此外这种用法利用消息传递数据的方法对有模式对话框和其他的窗口间通信也一样有效。
下载本节例子
5.4在对话框中进行消息映射
利用对话框的一个好处是可以利用ClassWizard对对话框中各个控件产生的消息进行映射,ClassWizrd可以列出各种控件可以使用的消息,并能自动产生代码。
在本节中我们以一个例子来讲解如何在对话框中对子窗口消息进行映射同时还讲解如何对对话框中的子窗口进行初始化。
首先我们产生编辑好一个对话框,如图,
在对话框中使用的控件和ID号如下表:
ID
类型
IDC_RADIO_TEST_1
圆形按钮
IDC_RADIO_TEST_2
圆形按钮
IDC_BUTTON_TEST
按钮
IDC_CHECK_TEST
检查按钮
IDC_TREE_TEST
树形控件
IDC_LIST_CTRL
ListCtrl
IDC_TAB_CTRL
TabCtrl
IDC_LIST_TEST
列表框
IDC_COMBO_TEST
组合框
IDC_EDIT_TEST
输入框
首先我们需要在对话框的OnInitDialog()函数中对各个控件进行初始化,这里我们使用CWnd*GetDlgItem(intnID)来通过ID号得到子窗口指针。
(类似的函数还有UINTGetDlgItemInt(intnID,BOOL*lpTrans=NULL,BOOLbSigned=TRUE)通过ID号得到子窗口中输入的数字,intGetDlgItemText(intnID,CString&rString)通过ID号得到子窗口中输入的文字)。
代码如下:
BOOLCMy54_s1Dlg:
:
OnInitDialog()
{
CDialog:
:
OnInitDialog();
/*添加初始化代码*/
//初始化输入框
((CEdit*)GetDlgItem(IDC_EDIT_TEST))->SetWindowText("thisisaeditbox");
//初始化列表框
CListBox*pListB=(CListBox*)GetDlgItem(IDC_LIST_TEST);
pListB->AddString("item1");
pListB->AddString("item2");
pListB->AddString("item3");
//初始化组合框
CComboBox*pCB=(CComboBox*)GetDlgItem(IDC_COMBO_TEST);
pCB->AddString("item1");
pCB->AddString("item2");
pCB->AddString("item3");
//初始化TabCtrl
CTabCtrl*pTab=(CTabCtrl*)GetDlgItem(IDC_TAB_TEST);
pTab->InsertItem(0,"TabPage1");
pTab->InsertItem(1,"TabPage2");
pTab->InsertItem(2,"TabPage3");
//初始化ListCtrl
CListCtrl*pList=(CListCtrl*)GetDlgItem(IDC_LIST_CTRL);
pList->InsertColumn(0,"Column1",LVCFMT_LEFT,100);
pList->InsertItem(0,"Item1");
pList->InsertItem(1,"Item2");
pList->InsertItem(2,"Item3");
//初始化TreeCtrl
CTreeCtrl*pTree=(CTreeCtrl*)GetDlgItem(IDC_TREE_TEST);
pTree->InsertItem("Node1",0,0);
HTREEITEMhNode=pTree->InsertItem("Node2",0,0);
pTree->InsertItem("Node2-1",0,0,hNode);
pTree->InsertItem("Node2-2",0,0,hNode);
pTree->Expand(hNode,TVE_EXPAND);
returnTRUE;//returnTRUEunlessyousetthefocustoacontrol
}
接下来我们需要利用ClassWizard对控件所产生的消息进行映射,打开ClassWizard对话框,选中相关控件的ID,在右边的列表中就会显示出可用的消息。
如我们对按钮的消息进行映射,在选中按钮ID(IDC_BUTTON_TEST)后,会看到两个消息,如图,
一个是BN_CLICKED,一个是BN_DOUBLECLICKED。
双击BN_CLICKED后在弹出的对话框中输入函数名,ClassWizard会产生按钮被按的消息映射。
然后我们看看对TabCtrl的TCN_SELCHANGE消息进行映射,如图,
在TabCtrl的当前页选择发生改变时这个消息会被发送,所以通过映射该消息可以在当前页改变时及时得到通知。
最后我们对输入框的EN_CHANGE消息进行映射,如图,
在输入框中的文本改变后该消息会被发送。
相关的代码如下:
//头文件中相关的消息处理函数定义
afx_msgvoidOnButtonTest();
afx_msgvoidOnSelchangeTabTest(NMHDR*pNMHDR,LRESULT*pResult);
afx_msgvoidOnChangeEditTest();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
//CPP文件中消息映射代码
ON_BN_CLICKED(IDC_BUTTON_TEST,OnButtonTest)
ON_NOTIFY(TCN_SELCHANGE,IDC_TAB_TEST,OnSelchangeTabTest)
ON_EN_CHANGE(IDC_EDIT_TEST,OnChangeEditTest)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//消息处理函数
voidCMy54_s1Dlg:
:
OnButtonTest()
{
AfxMessageBox("youpressedabutton");
}
voidCMy54_s1Dlg:
:
OnSelchangeTabTest(NMHDR*pNMHDR,LRESULT*pResult)
{
TRACE("TabSelectchanged\n");
*pResult=0;
}
voidCMy54_s1Dlg:
:
OnChangeEditTest()
{
TRACE("edit_boxtextchanged\n");
}
对于其他的控件都可以采取类似的方法进行消息映射,下载例子。
此外如果你对各种控件可以使用的消息不熟悉,你可以通过使用对话框,然后利用ClassWizard产生相关代码的方法来进行学习,你也可以将ClassWizard产生的代码直接拷贝到其他需要的地方(不瞒你说,我最开始就是这样学的:
-D这也算一个小窍门)。
5.5在对话框中进行数据交换和数据检查
MFC提供两种方法在对话框中进行数据交换和数据检查(Dialogdataexchange/Dialogdatavalidation),数据交换和数据检查的思想是将某一变量和对话框中的一个子窗口进行关联,然后通过调用BOOLUpdateData(BOOLbSaveAndValidate=TRUE)来指示MFC将变量中数据放入子窗口还是将子窗口中数据取到变量中并进行合法性检查。
在进行数据交换时一个子窗口可以和两