MFC DoModal 对话框

5

好的,我承认我对Windows API甚至MFC都没有了解。

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

但是我无论如何也想不通为什么它在调用doModal时崩溃了。

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

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

Am I just being borderline retarded?


3
在调用DoModal时,应该使用 CDialog BadFileD; 而不是 CDialog *BadFileD = new CDialog();。这样更简单,并且你不必担心在完成后删除指针。请注意,两者之间的区别在于一个是实例化 CDialog 对象,而另一个是动态地分配并实例化 CDialog 指针。 - Mark Ransom
3个回答

23

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

(1) 模态对话框的用法:

CDialog dlg;
dlg.DoModal();

(2)无模式对话框的使用:

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

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

  1. OnOk()OnCancel() 中添加 DestroyWindow() 方法。
  2. PostNcDestroy() 方法中添加 "delete this;"。

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


10

为显示模态对话框,您应该仅使用 DoModal 方法。

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

你可以从文章中阅读备注


感谢您的回答。我在我的代码中混淆了CreateDoModal,这是为一些简单的子窗口。应用程序似乎会在打开和关闭弹出窗口后很长时间崩溃。例如,在更改Windows桌面主题时。非法指令异常将在WalkpreTranslateTree内的某个地方发生。删除不需要的Create语句后,一切恢复正常。 - E. van Putten

1
如果您只想显示错误信息,可能可以使用 AfxMessageBox() 而不是创建自己的对话框。请参见 Microsoft Developer Network - AfxMessageBox
如果要创建自己的对话框框架,通常需要:
- 使用资源编辑器创建对话框模板 - 使用类向导创建封装对话框的类,并实现所需的行为 - 将代码插入到适当的位置以创建和显示对话框
但是,对于不需要复杂行为支持类的简单对话框,可以跳过使用类向导创建封装类的步骤,直接使用 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()成员函数(实际上是从中派生CDialogCWnd类的成员函数)来显示或隐藏它。
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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接