弹出对话框未显示在其他窗口之上

27

我正在我的WPF(MVVM)应用程序中使用Window.ShowDialog()打开一个模态窗口,但是它允许我使用Windows任务栏(Windows 7)导航到其他窗口。

考虑这个问题: 我在我的应用程序中打开了3个非模态窗口。现在其中一个使用Window.ShowDialog()打开了一个模态窗口。我还将Application.MainWindow设置为模态窗口的所有者。这是因为我正在使用MVVM消息处理程序,用于打开新窗口的消息处理程序集中在App.xaml.cs中。窗口以模态方式打开——没有问题。然而,Windows 7允许我从任务栏切换到其他应用程序窗口。这会导致模态窗口被另一个窗口覆盖,我不想要这种情况。

只要我打开了模态窗口,我就无法在其他窗口上执行任何操作,但如果模态窗口一直保持在最前面,那就太好了。是否有一种方法可以在模态打开时禁用任务栏切换?FYI——从应用程序启动的所有打开窗口都显示为任务栏上的单独条目。

提前致谢!


我们能否获取一些代码,用于创建成为模态对话框的窗口? - user7116
你需要的是窗口在所有其他应用程序之上。而我需要的是窗口在应用程序中任何其他窗口之上,就像对话框窗口一样。对于我的要求,这两行代码:Owner = Application.Current.MainWindow; 和 ShowInTaskbar = false; 很好地解决了问题。给你点赞。 - Fandi Susanto
5个回答

63

没有任何代码可以作为基础,但是看起来你在创建Window时遗漏了一些属性,并期望ShowDialog应用额外的“对话框”语义:

Window window = new Window()
{
    Title = "Modal Dialog",
    ShowInTaskbar = false,               // don't show the dialog on the taskbar
    Topmost = true,                      // ensure we're Always On Top
    ResizeMode = ResizeMode.NoResize,    // remove excess caption bar buttons
    Owner = Application.Current.MainWindow,
};

window.ShowDialog();

你是救星sixlettervariables!Topmost就是我所缺失的。很抱歉没有发布代码,但你解决了问题。简直不敢相信我竟然错过了它。再次感谢! - Pratz
要删除标题按钮,您需要将其“ResizeMode”设置为“NoResize”,而不是“WindowStyle”。 ToolWindow不是对话框。 - David Anderson
@DavidAnderson:听起来不错,只要你不介意调整对话框的大小。 - user7116
是的,这个观点很好,但是你也可以使用Win32 API来移除标题按钮并保持调整大小的功能,我想WPF仍然存在一些问题。不过这只是代码而已,总有一种方法可以做到某事。 - David Anderson
但是 System.Windows.Window.ShowDialog() 不接受任何参数。为什么? - J Pollack

13

只需将窗口的所有者属性设置为调用窗口即可。这样,激活WPF应用程序在任务栏中不仅会激活主窗口,还会激活模态子窗口并将其置于前台。

ChildWindow C = new ChildWindow();
C.Owner = this;
C.ShowDialog();

12

将Topmost设置为True(Topmost=True)会导致对话框在系统中处于所有窗口的顶部(不仅适用于应用程序内)。

因此,您可以尝试在主窗口中注册Activated事件:

Activated += WindowActivated;

每当您应用程序的另一个主窗口变为活动状态时,激活模态对话框的所有者窗口。

private void WindowActivated(object sender, EventArgs e)
{
    Window window = Application.Current.Windows.OfType<YourMainWindow>().FirstOrDefault(p => p != this && !p.IsActive && p.OwnedWindows.Count > 0);
    if (window != null)
    {
        window.Activate();
    }
}

这个简单的方法假设你的所有子窗口都是模态的,但你也可以编写更复杂的逻辑。


1
这应该是被接受的答案:似乎唯一的方法是在使用任务栏激活应用程序后将对话框窗口保持在主窗口之上。这是一个已知的问题:http://connect.microsoft.com/VisualStudio/feedback/details/474480/wpf-showdialog-and-owner - stijn
这在对话框的代码后端中更有效(使用事件的+=)。这允许主窗口不关心其他窗口正在做什么。 - Grault
哦,而且在对话框隐藏时激活它(因为我不想真的关闭它)会使主窗口无法操作,所以需要检查IsVisible。 - Grault

4

我最终采用了几个答案的组合。一开始接受的答案很有用,但正如这里其他人指出的那样,设置Topmost = true意味着窗口始终在运行的任何其他应用程序之上。我的解决方案是:

var myWindow = new MyWindowType();
myWindow.Owner = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);

我最初使用的是:

myWindow.Owner = Application.Current.MainWindow;

然而,如果您像这样打开了三个窗口,则使用此方法会导致问题:
MainWindow
   |
   -----> ChildWindow1

               |
               ----->  ChildWindow2

设置ChildWindow2.Owner = Application.Current.MainWindow将把窗口的所有者设置为其祖父窗口,而不是父窗口。

为了加快速度,我已在Visual Studio中添加代码片段。如果您将以下内容添加到工具 --> 代码片段管理器 --> 我的代码片段:

<CodeSnippets
    xmlns="http://schemas.microsoft.com/VisualStudio/2010/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>MVVM Set owner of page to be current active window</Title>
      <Shortcut>owner</Shortcut>
    </Header>
    <Snippet>
      <Code Language="CSharp">
        <![CDATA[System.Windows.Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

输入'owner'并双击Tab键,将自动为您添加'Application.CurrentWindows...'的部分。

1
这个程序救了我的命,因为我设置了所有其他人提到的选项,但从未注意过mainwindow可能是什么。你的答案让我开始研究它。 - Dirty Developer

1

需要进行一些修改。 我必须设置所有者并激活窗口。 按照以下方式检查弹出窗口并激活窗口。

        var enumerator = Application.Current.Windows.GetEnumerator();
        while (enumerator.MoveNext())
        {
            Window window = (Window)enumerator.Current;
            if (window != null && window.GetType() == typeof(PopUpWindow))
            {
                window.Activate();
            }
        }

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