MFC DoModal 对话框

Posted

技术标签:

【中文标题】MFC DoModal 对话框【英文标题】:MFC DoModal Dialog 【发布时间】:2011-06-30 00:14:18 【问题描述】:

好吧,我承认我对 Windows API 甚至 MFC 都一无所知。

当事情变得多毛(文件名字符串中的非法字符)时,我会弹出一个错误窗口,我希望错误框是模态的。

对于我的生活,我无法弄清楚为什么它在点击 doModal 时会崩溃。

这是我认为可以修复的代码。此代码位于主窗口中按钮的事件处理程序中。

CDialog *BadFileD = new CDialog();
BadFileD->Create(IDD_STATUS, this); 
BadFileD->DoModal();

我是不是有点智障?

【问题讨论】:

应该使用CDialog BadFileD; 而不是CDialog *BadFileD = new CDialog();,尤其是在调用DoModal 时。更简单,完成后不用担心删除指针。 【参考方案1】:

MFC对话框分两种模式,模态对话框和非模态对话框。

(1) 模态对话框用法:

CDialog dlg;
dlg.DoModal();

(2) 无模式对话框用法:

CMyDialog *pDlg = new CMyDialog();
pDlg->Create(ID_DLG, this);
pDlg->ShowWindows(SW_SHOW);

如您所见,我们需要一个新指针,但不要删除它。因此,您需要在我们的CMyDialog 类中执行以下操作:

    OnOk()OnCancel()中添加DestroyWindow()方法。 在PostNcDestroy()方法中添加“delete this;”。

如果不这样做,您的代码可能会导致内存泄漏。 BadFileD 是一个类成员,你在析构函数中删除它。我建议使用无模式对话框。

【讨论】:

【参考方案2】:

对于显示模式对话框,您应该只使用 DoModal 方法

CDialog *BadFileD = new CDialog(IDD_STATUS, this);
BadFileD->DoModal();

您可以阅读article的评论

【讨论】:

感谢您的回答。我在代码中混合了CreateDoModal,用于一些简单的子窗口。打开和关闭该弹出窗口后,该应用程序似乎会随机崩溃很长时间。例如在更改 Windows 桌面主题时。在WalkpreTranslateTree 的某个地方会发生非法指令异常。删除不需要的Create 语句后,一切恢复正常。【参考方案3】:

如果您只想显示一条错误消息,您可以使用AfxMessageBox(),而不是创建自己的对话框。见Microsoft Developer Network - AfxMessageBox。

如果您想使用 MFC 项目创建自己的对话框,通常会:

使用资源编辑器创建对话框模板 使用实现所需行为的类向导创建一个封装对话框的类 将用于创建和显示对话框的代码插入到适当的位置

但是,对于复杂行为不需要支持类的简单对话框,您可以跳过使用类向导创建封装类的步骤,直接使用CDialog

需要回答的一个问题是对话框的生命周期以及它是模态的还是非模态的。模态对话框需要用户做一些事情,以使应用程序继续通过模态对话框。无模式对话框不会像模式对话框那样阻塞应用程序。还有系统模态对话框样式。

既然你说它是一个模态对话框,那么它的生命周期会很短,所以整个构建、显示和销毁可能会在一系列代码行中完成。例如,在带有显示模式对话框的命令处理程序的 CView 类中,您可能有:

void CViewThing::OnCommandMenuItem ()

    CDialog BadFileD(IDD_STATUS);
    int iRetStatus = BadFileD.DoModal();
    // check for status such as IDOK, etc.
    // do whatever is necessary.

上面所做的就是使用对话框资源模板IDD_STATUS创建一个对话框,并将其显示为一个模态对话框。由于是本地对象,当变量BadFileD超出范围时,会触发对话框析构函数,为你清理资源。

你也可以有一个无模式的对话框。在无模式对话框的情况下,您需要考虑变量的生命周期,因为一旦变量超出范围,析构函数将触发并且对话框将消失。

因此,对于与某个视图类一起使用的无模式对话框,可能提供某种工具箱,CDialog 变量将是使用它的CView 类的成员。无模式对话框创建后,通过CDialog类的成员函数ShowWindow()(实际上是CWnd派生CDialog的成员)的成员函数显示与否。

void CViewThing::OnCommandMenuItem ()

    BadFileD.Create(IDD_STATUS, this);
    BadFileD.ShowWindow(SW_SHOW);   // display the dialog

CViewThing 类中,您将拥有一个成员变量CDialog BadFileD;

其他注意事项

在上述所有示例中,我们都没有使用指针,因此当CDialog 变量超出范围时,无论是退出成员函数还是使用对话框的对象被销毁时,对话框也是如此.这个对象管理是为我们完成的。

对于无模式对话框,您必须考虑的一件事是当您不再需要它时如何将其销毁。

由于模态对话框通常是一个短期对象,通常作为堆栈上的局部变量创建,因此您通常只需让它超出范围以处理与销毁有关的所有事情。

但是,无模式对话框的生命周期要求在不再需要对话框时使用DestroyWindow() 方法将其销毁。见Microsoft Developer Network - Destroying the Dialog Box。

第三种使用场景——嵌入对话框

对话框的第三种用法有时会派上用场,将对话框作为控件嵌入到另一个窗口中。

在上述示例中,对话框模板为对话框指定了WS_POPUP 样式,这是对话框的标准样式,因为使用对话框的正常方式是显示为单独的窗口。

但是,如果您将WS_POPUP 样式更改为WS_CHILD,则可以将对话框作为控件嵌入到另一个窗口中。您可以删除其他样式设置,例如WS_SYSMENUDS_MODALFRAMEWS_CAPTION,并从对话框模板中删除CAPTION 行以进一步更改对话框。所以你最终会得到类似的东西:

IDD_STATUS DIALOGEX 0, 0, 435, 266
STYLE DS_SETFONT | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    LTEXT           "this is some static text to display on this dialog.",IDC_STATIC,81,63,200,32
END

然后只需使用生成的对话框,类似于使用ShowWindow() 的无模式对话框。

如果您需要在其容器窗口中重新定位嵌入的对话框,您可以使用SetWindowPos() 方法来执行此操作。例如,以下内容会将对话框窗口在其包含窗口内移动到距包含窗口左侧 20 像素和距包含窗口顶部 10 像素的位置。

BadFileD.SetWindowPos(NULL, 20, 10, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER)

【讨论】:

以上是关于MFC DoModal 对话框的主要内容,如果未能解决你的问题,请参考以下文章

MFC(C++)CDialog DoModal()没有按预期工作

在启动前隐藏一个MFC DoModal对话框

从 CDialog 派生的对话框在 DoModal (MFC/C++) 之后返回 -1

如何初始化模态对话框? (C++/MFC)

MFC Domodal 返回-1的一种情况

MFC Domodal 返回-1的一种情况