MFC模式对话框ESCAPE和ENTER退出问题 - 小众知识

MFC模式对话框ESCAPE和ENTER退出问题

2015-08-08 10:52:18 苏内容
  标签: MFC/Dialog
阅读:4157

首先资源列表中按下右键,可以在弹出菜单中选择“插入对话框”。然后再打开该对话框进行编辑,你会在屏幕上看到一个控件板。你可以将所需要添加的控件拖到对话框上,或是先选中后再在对话框上用鼠标画出所占的区域。

对于有模式对话框使用DoModal()产生,对于无模式对话框使用Create()产生。
相关实例代码如下;

void CMy51_s1View::OnCreateDlg()
{//产生无模式对话框
CTestDlg *dlg=new CTestDlg;
dlg->Create(IDD_TEST_DLG);
dlg->ShowWindow(SW_SHOW);
}

void CMy51_s1View::OnDoModal()
{//产生有模式对话框
CTestDlg dlg;
int iRet=dlg.DoModal();
TRACE("dlg return %d/n",iRet);
}

如果你在调试这个程序时你会发现程序在退出后会有内存泄漏,这是因为没有释放无模式对话框所使用的内存。

关于在使用对话框时Enter键和Escape键的处理:在使用对话框是你会发现当你按下Enter键或Escape键都会退出对话框,这是因为Enter键会引起CDialog::OnOK()的调用,而Escape键会引起CDialog::OnCancel()的调用。而这两个调用都会引起对话框的退出。在MFC中这两个成员函数都是虚拟函数,所以我们需要进行重载,如果我们不希望退出对话框那么我们可以在函数中什么都不做,如果需要进行检查则可以添加检查代码,然后调用父类的OnOK()或OnCancel()。相关代码如下;

void CTestDlg::OnOK()
{
AfxMessageBox("你选择确定");
CDialog::OnOK();
}

void CTestDlg::OnCancel()
{
AfxMessageBox("你选择取消");
CDialog::OnCancel();
}

2 创建有模式对话框

使用有模式对话框时在对话框弹出后调用函数不会立即返回,而是等到对话框销毁后才会返回(请注意在对话框弹出后其他窗口的消息依然会被传递)。所以在使用对话框时其他窗口都不能接收用户输入。创建有模式对话框的方法是调用CDialog::DoModal()。下面的代码演示了这种用法:

CYourView::OnOpenDlg()
{
CYourDlg dlg;
int iRet=dlg.DoModal();
}

CDialog::DoModal()的返回值为IDOK,IDCANCEL。表明操作者在对话框上选择“确认”或是“取消”。由于在对话框销毁前DoModal不会返回,所以可以使用局部变量来引用对象。在退出函数体后对象同时也会被销毁。而对于无模式对话框则不能这样使用,下节3 创建无模式对话框中会详细讲解。

你需要根据DoModal()的返回值来决定你下一步的动作,而得到返回值也是使用有模式对话框的一个很大原因。

使用有模式对话框需要注意一些问题,比如说不要在一些反复出现的事件处理过程中生成有模式对话框,比如说在定时器中产生有模式对话框,因为在上一个对话框还未退出时,定时器消息又会引起下一个对话框的弹出。

同样的在你的对话框类中为了向调用者返回不同的值可以调用CDialog::OnOK()或是CDialog::OnCancel()以返回IDOK或IDCANCEL,如果你希望返回其他的值,你需要调用
CDialog::EndDialog( int nResult );其中nResult会作为DoModal()调用的返回值。

下面的代码演示了如何使用自己的函数来退出对话框:

void CMy52_s1View::OnLButtonDown(UINT nFlags, CPoint point)
{//创建对话框并得到返回值
CView::OnLButtonDown(nFlags, point);
CTestDlg dlg;
int iRet=dlg.DoModal();
CString szOut;
szOut.Format("return value %d",iRet);
AfxMessageBox(szOut);
}
//重载OnOK,OnCancel
void CTestDlg::OnOK()
{//什么也不做
}
void CTestDlg::OnCancel()
{//什么也不做
}
//在对话框中对三个按钮消息进行映射
void CTestDlg::OnExit1()
{
CDialog::OnOK();
}
void CTestDlg::OnExit2()
{
CDialog::OnCancel();
}
void CTestDlg::OnExit3()
{
CDialog::EndDialog(0XFF);
}

由于重载了OnOK和OnCancel所以在对话框中按下Enter键或Escape键时都不会退出,只有按下三个按钮中的其中一个才会返回。 点击窗口右上角红色x按钮回产生OnClose事件,可以在这里实现退出。

此外在对话框被生成是会自动调用BOOL CDialog::OnInitDialog(),你如果需要在对话框显示前对其中的控件进行初始化,你需要重载这个函数,并在其中填入相关的初始化代码。利用ClassWizard可以方便的产生一些默认代码,首先打开ClassWizard,选择相应的对话框类,在右边的消息列表中选择WM_INITDIALOG并双击,如图,ClassWizard会自动产生相关代码,代码如下:

BOOL CTestDlg::OnInitDialog()
{

CDialog::OnInitDialog();
 
return TRUE;
}

 


其实大家都明白,想要解决这个问题,其实有两种思路:

1、处理PreTranslateMessage函数,将消息为ESC和ENTER的都过滤掉,如直接返回或替换为WM_RETURN等。但是这个不太推荐。

2、重载OnOk(),OnCancel()函数,(这是根本的重点的解决办法)

     对于很熟悉VC的同学来说不难,但是初入门的在网上看到的都是如何如何的办法,没有将新手最迫切的问题解决了:如何重载?

首先,MFC创建的Dialog在按下默认的确定(OK)和取消(CANCEL)按钮时都点用了父窗口的OnOk()和OnCancel()函数,他们的区别:
第一,OnOK()和OnCancel()是CDialog基类的成员函数,而OnClose()和OnDestroy()是CWnd基类的成员函数,即WM消息响应函数。从应用程序结构的角度,拿对话框来说,红色的X对应的是CWnd,而处于对话框中的“确定”、“取消”按钮则对应了CDialog。
第二,OnClose()和OnDestroy()
在单视图程序中,根据<<深入浅出MFC>>所讲,程序退出时执行的操作顺序为(从点X按钮开始)
(1)用户点击X退出按钮,发送了WM_CLOSE消息----->响应OnClose()
(2)在WM_CLOSE消息的处理函数中,调用DestroyWindow()----->销毁与指定CWnd窗口对象关联的窗口,但未销毁CWnd对象
(3)在DestroyWindow()中发送了WM_DESTROY消息----->窗口销毁后响应OnDestroy()
(4)在WM_DESTROY消息中调用PostQuitMessage(),发送WM_QUIT消息,结束消息循环
可以看到,程序的退出过程,是先响应OnClose(),然后响应OnDestroy(),在响应OnDestroy()之前,窗口对象已经被销毁。OnDestroy()到底干了什么呢?它就像一个teller,先通知CWnd对象告诉它即将被销毁,尔后OnDestroy的真正运行是在CWnd对象已经从屏幕上清除以后被调用的。
第三,OnOK()、OnCancel()()、OnClose()、OnDestroy()
CDialog::OnOK首先调用UpdateData(TRUE)将数据传给对话框成员变量,然后调用CDialog::EndDialog关闭对话框;  
CDialog::OnCancel只调用CDialog::EndDialog关闭对话框;  
OnClose()是响应   WM_CLOSE   的.一定程度上可以说CDialog::EndDialog()和OnClose()完成类似的工作,但处理的机制不一样,前者是CDialog的对象机制,后者是WM的消息映射机制。
CDialog::EndDialog()-------->OnDestroy()
                 OnClose()-------->OnDestroy()
EndDialog()和OnClose()属于“同级别”的,所以我们在按下OK按钮的时候,程序是不会执行OnClose()的,但两种机制都必须经过OnDestroy()
对于按键:
按esc只会调用OnCancel()
按alt+f4会先调用OnClose()后调用OnCancel()
按X或关闭按扭时会先调用OnClose()后调用OnCancel()

**********************************************************************************

重载OnOK,OnCancel函数,下面的这种方式不是很正确:

protected:
virtual void OnCancel();
virtual void OnOK();


void CDialog::OnOK()
{
// TODO: 在此添加专用代码和/或调用基类

//CDialog::OnOK();
}
.....

**********************************************************
      其实也没啥太大的错误,最大的错误是重载了CDialog的OnOk,OnCancel方法,这样的话是对于父窗口的正常使用有潜在的危险,并且经实践有时候并不能够达到原有的目的。最正确的方法是重载CXXXDlg(即我们需要处理的窗口)的对应方法,这样就很完美了。
      一般来说,我们需要不但需要重载OnOk(),OnCancel(),而且需要重新相应WM_CLOSE消息,其中WM_CLOSE消息相应很重要,如果不这么做你就不能通过X以及各种正常的方式关闭对话框。
      所以一般这么做:
 
[cpp] view plaincopy
//CXXXDlg.h中 
protect://public也没错 
virtual void OnOk(); 
virtual void OnCancel(); 
 
//CXXXDlg.cpp中 
 
void CXXXDlg::OnOk()//啥也不做就OK了 

 

void CXXXDlg::OnCancel()//同上 
 

 

然后Add window message Handle,选则WM_CLOSE消息,添加并编辑,在消息响应中函数中添加
 
 
Enddialog(IDCANCEL); //参数可以自己根据需要填写
如,我的项目名叫CDlgDrawTool,代码如下:
 
[cpp] view plaincopy
void CDlgDrawTool::OnCancel()   //回车按钮处理 


void CDlgDrawTool::OnOK()   //ESC按钮不处理 


void CDlgDrawTool::OnClose()    //X按钮处理 

    // TODO: Add your message handler code here and/or call default 
    EndDialog(IDCANCEL);    //关闭窗口 
    CDialog::OnClose(); 

扩展阅读
相关阅读
© CopyRight 2010-2021, PREDREAM.ORG, Inc.All Rights Reserved. 京ICP备13045924号-1