【问题标题】:Using Windows functions to make a window modal使用 Windows 函数制作窗口模式
【发布时间】:2011-07-31 10:34:19
【问题描述】:

我正在使用 c#,我想让一个窗口成为模态窗口,我想为此使用 Windows API 中的方法。 SetWindowPos 函数可以使窗口成为最顶层的窗口,但我找不到使其成为模态的方法...

有没有办法用 SetWindowPos 做到这一点,如果没有,有没有其他窗口函数可以做到这一点?

【问题讨论】:

  • 如果您不需要使用 Windows API 来完成此类事情,请不要这样做。否则,您可能不会在 .NET Framework 上进行开发。
  • 我想将调用GetSaveFileName windows函数创建的窗口设为模态
  • 您有什么理由不想使用现有的托管方式来执行此操作?比如Form.ShowDialog()
  • 因为我已经使用 HookProc 自定义了 savefiledialog...

标签: c# .net winforms winapi modal-dialog


【解决方案1】:

如果不是在创建时,您将无法使其成为模态。但是你可以通过禁用它的 parent 所有者来达到类似的效果。

【讨论】:

  • 更准确地说,您将禁用它的所有者。父窗口和所有者窗口之间的区别在文档中并不总是很清楚,尽管它很重要。 Raymond Chen 提供更多信息here
  • 糟糕。我站得更正了。 Owner 是我真正的意思:模态窗口显然没有父级。
【解决方案2】:

考虑到您的问题被标记为,您可以使用整个 .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;

【讨论】:

  • 实际上,当我自定义了我的 savefiledialog 时,我自己实现了 ShowDialog 方法,在其中,我调用 GetSaveFileName 来创建 savefiledialog。所以我想在我的 ShowDialog 方法中添加该模式功能..任何答案???
  • @dia:我不明白你为什么要自己重新实现ShowDialog 方法。你的意思是你创建了一个单独的表单,你调用 而不是 SaveFileDialog,而那个 那个 表单负责调用/显示保存对话框? Windows API 函数GetSaveFileNameOPENFILENAME 结构作为参数,其中一个成员是hwndOwner。如果您在此处指定正确的窗口句柄,它将自动以该窗口作为其所有者以模态方式显示对话框。
  • @dia:使用它来指定 main 窗口的句柄,而不是包装对 GetSaveFileName 的调用的句柄。我怀疑您 当前 正在传递该窗口的句柄,因此该函数正在做正确的事情并禁用该窗口。除此之外,您不在乎该窗口是否被禁用。
  • 是的,你是对的!!!为此非常感谢!我必须自定义保存文件对话框才能为其添加一些复选框......所以我正在实现一个新的用户控件。但是你能解释一下识别那个表格的方法吗???我认为的一种方法是在创建用户控件的对象时发送主窗体的句柄......
  • 非常感谢您的回答!!!效果很好!!!我也跟着这个:stackoverflow.com/questions/1163435/c-method-caller。两种方法都很好用...再次非常感谢您...
猜你喜欢
  • 2011-01-30
  • 1970-01-01
  • 2012-06-05
  • 1970-01-01
  • 1970-01-01
  • 2012-07-19
  • 2011-10-31
  • 2019-08-16
  • 1970-01-01
相关资源
最近更新 更多