【问题标题】:WPF window should be modal to native owner window, but isn'tWPF 窗口应该是本机所有者窗口的模式,但不是
【发布时间】:2012-12-18 12:45:02
【问题描述】:

我在 WPF 项目中有以下 C# 代码:

private static void RunConfig(string owner)  
{  
    long ownerHandle;  
    var settingsWindow = new SettingsWindow();  
    if (long.TryParse(owner, out ownerHandle))  
    {  
        WindowInteropHelper helper = new WindowInteropHelper(settingsWindow);  
        helper.Owner = new IntPtr(ownerHandle);  
    }  
    settingsWindow.ShowDialog();  
}

SettingsWindow 与所有者窗口的模式不正确(即,我可以在 SettingsWindow 仍处于打开状态时关注所有者窗口、与之交互甚至关闭所有者窗口)。我做错了什么?

就上下文而言,此代码是屏幕保护程序的一部分,所有者窗口是控制面板屏幕保护程序选择窗口(通过命令行参数传入句柄以用作所有者)。我知道 IF 语句正在评估 true 并正确解析句柄。

我还尝试使用user32.dll 中的SetWindowLongPtr 方法(针对x64 编译,因此不使用SetWindowLong),该方法在here 中进行了简要描述,并在使用here 中显示。此方法适用于 WinForms,但似乎不适用于 WPF。帮助我欧比旺克诺比,你是我唯一的希望。

【问题讨论】:

  • SettingsWindow 是一个winforms 窗口吗?因为如果SettingsWindow 也是 WPF,ShowDialog() 应该阻止访问您的主 WPF 表单
  • 不,SettingsWindow 是 WPF 窗口。我的程序没有打开任何其他窗口,没有“主”窗口。控制面板屏保选择窗口,是一个原生窗口,是应该设置为settingsWindow所有者的窗口。 ShowDialog() 确实阻止了我程序的 UI 线程,但这不是问题。我需要 settingsWindow 来防止焦点和与控制面板窗口的交互,直到 settingsWindow 关闭(即modal window
  • 我注意到,当使用 WindowInteropHelper 设置窗口的所有者时,窗口现在总是在所有者之上,即使所有者仍然可以接收焦点并与之交互。所以它正在做一些事情,而不是它应该做的一切......

标签: c# wpf window native owner


【解决方案1】:

事实证明,使用WindowInteropHelper 将本机窗口设置为 WPF 窗口的所有者确实有效,但它并不能完成全部工作。以这种方式设置时,WPF 窗口将在本机窗口顶部保持可见,即使本机窗口具有焦点。然而,这是获得的唯一效果。 WPF Window 不会阻​​止与原生 Window 的交互,甚至可以关闭原生窗口,而 WPF Window 不会关闭或受到影响。

为了获得所需的其余行为,我们需要在 WPF 窗口上调用 ShowDialog 之前使用 user32.dll 中的 EnableWindow 函数禁用本机窗口,然后再次重新启用它一次WPF 窗口关闭。

修改后的代码如下:

private static void RunConfig(string owner)
{
    long ownerHandle;
    var settingsForm = new SettingsWindow();
    if (long.TryParse(owner, out ownerHandle))
    {
        WindowInteropHelper helper = new WindowInteropHelper(settingsForm);
        helper.Owner = new IntPtr(ownerHandle);
        NativeMethods.EnableWindow(helper.Owner, false);
        settingsForm.ShowDialog();
        NativeMethods.EnableWindow(helper.Owner, true);
    }
    else
    {
        settingsForm.ShowDialog();
    }
}

(注意:上面的代码一般是正确的,但在屏幕保护程序的情况下是不完整的,这就是这段代码的实际用途。如果这段代码被用于屏幕的配置窗口saver, 为所有者句柄传递的字符串不是要用作所有者的控制面板窗口的句柄,而是控制面板窗口的子控件的句柄。在这种情况下,额外的步骤是获取该控件的父控件的句柄。我们可以通过在传入的句柄上调用GetParent(也在user32.dll 中)来做到这一点。这将返回我们想要用于所有者和EnableWindow 的真实句柄电话。)

如果来自 Microsoft 的任何人发现此问题,可能考虑修改 WindowInteropHelper 以在分配 Owner 并使用 ShowDialog 时正确设置所有这些,因为这是模式窗口的正确完整行为。

【讨论】:

    猜你喜欢
    • 2010-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-25
    • 2011-02-05
    • 1970-01-01
    • 2011-03-06
    • 2010-10-05
    相关资源
    最近更新 更多