考虑到您的问题被标记为c#,您可以使用整个 .NET Framework,它提供了将窗口(表单)显示为模式对话框的内置功能。您没有理由必须从 Windows API 调用函数来执行此操作。
通常,从 Windows API P/Invoke 函数的唯一原因是使用尚未通过托管代码公开的功能。一般的应用程序很少需要这样做,而基本用例(如显示模式对话框)当然不需要它。
在 C#(或任何 .NET 语言)中执行此操作的方法是更改显示相关表单的方式。您应该使用Form.ShowDialog method,而不是调用Form.Show 方法。
有两个重载可用。 first 不接受任何参数,并将对话框的所有者设置为当前活动窗口。 second 接受一个参数,该参数指定将拥有此对话框的窗口。
Form.Modal property 提供了一种快速检查表单是否以模态方式显示的方法。但是,正如文档所述,它是只读的。模态显示表单的唯一方法是调用ShowDialog 方法,如上所述。
就 Windows API 解决方案而言,没有任何可以调用的此类函数。窗口在创建后不能转换为模式对话框。您最初必须以这种方式显示它。在 Win32 应用程序中,您可以通过调用 DialogBox function(而不是 CreateDialog)来实现。
MFC 使用CDialog::DoModal function 显示模式对话框,但它并没有真正显示模式对话框。相反,它使用 hack 来创建 伪(或模拟)模式对话框,包括禁用所有者窗口、在 DoModal 方法中运行自己的消息循环,并等待预定义的系列导致模态循环正常退出的事件。我不建议在您自己的应用程序中执行此操作。这根本没有必要,而且太容易出错。 MFC 方法中存在/已经足够多的错误,并且该团队完全了解 Win32 内部结构。
WinForms 中的ShowDialog 方法在与MFC 非常相似的模型上工作。 Hans 的回答here 提供了更多信息。请注意,如果您要实现自己的模态对话框循环,您需要记住重新启用
首先拥有主窗口,然后销毁模态对话框。如果您没有按正确的顺序执行此操作,您最终会得到错误的焦点窗口。微软的 Raymond Chen 发表了一篇关于此的博客文章:The correct order for disabling and enabling windows。
编辑:添加了更多信息,基于来自 asker 的 cmets。
我不确定您为什么需要创建的包装器来将保存对话框自定义为UserControl。它不是一个控件,用户不会在你的表单上与它交互。你只会从你的代码中展示它。因此,我认为更好的选择是创建一个新类,将其称为SaveDialogHelper,并公开一个名为ShowSaveDialog 或类似名称的静态方法。例如:
public static class SaveDialogHelper
{
public string ShowSaveDialog(IWin32Window owner)
{
// Fill the OPENFILENAME struct, and do any necessary customizations
// ...
// Show the save dialog
// ...
// Return the path that was selected by the user
// ...
}
}
请注意,ShowSaveDialog 函数接受 IWin32Window 类型的单个参数 (owner),就像 .NET Framework 中的 ShowDialog 方法一样。当您调用该函数时,只需指定您想要拥有该对话框的表单:
SaveDialogHelper.ShowSaveDialog(this);
在函数内部,您可以使用Handle property 提取窗口句柄(Win32 术语中的hwnd),并相应地设置OPENFILENAME 结构的hwndOwner 成员:
hwndOwner = owner.Handle;