【问题标题】:WPF open popup window and work on the main window while the popup is openWPF 打开弹出窗口并在弹出窗口打开时在主窗口上工作
【发布时间】:2025-04-28 07:30:02
【问题描述】:

我是 WPF 新手。在我的应用程序中,当在主窗口中单击显示图表按钮时,我必须在弹出窗口中显示图表。当弹出窗口打开并显示时,用户应该能够更改主窗口中的值,以便图表自动更新。

问题是,当用户在主窗口中工作时,如何显示弹出窗口。 Window.ShowDialog() 将不允许在弹出窗口打开时在主窗口中工作。而当我使用 Window.Show() 时,当焦点设置到主窗口时,弹出窗口会退到后面并且不显示图表。

如何让用户在弹出窗口显示内容时在主窗口中工作。

提前致谢

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    不应使用Window.ShowDialog() 打开弹出窗口,因为这会使它成为模态(在关闭之前您将无法返回主窗口。)

    如果您使用Window.Show() 打开弹出窗口,您可以通过将TopMost 设置为true 来告诉弹出窗口保持领先地位。

    var popup = new PopupWindow();
    popup.Show();
    popup.TopMost = true;
    

    之后,您甚至可以通过在该窗口上调用 .Activate() 将焦点拉回到主窗口。

    【讨论】:

      【解决方案2】:

      您可以将图表嵌套在弹出窗口中而不是窗口中,然后像这样启动它

      <Button x:Name="button" Click="button_Click"/>
      <Popup x:Name="popup" StaysOpen="true">
          <TextBlock Text="Clicked"/>
      </Popup>
      
      private void button_Click(object sender, RoutedEventArgs e)
      {
          if (!popup.IsOpen) popup.IsOpen = true; // Open it if it's not open
          else popup.IsOpen = false; // Close it if it's open
      }
      

      article 描述了您的更多选择。

      或者,您可以尝试在单独的 UI 线程中启动 Windows,就像 article 建议的那样。像这样的

      <Button x:Name="button" Click="button_Click"/>
      
      private void button_click(object sender, RoutedEventArgs e)
      {
          Thread thread = new Thread(() =>
          {
              MyWindow w = new MyWindow();
              w.Show();
      
              w.Closed += (sender2, e2) =>
              w.Dispatcher.InvokeShutdown();
      
              System.Windows.Threading.Dispatcher.Run();
          });
      
          thread.SetApartmentState(ApartmentState.STA);
          thread.Start();
      }
      

      弹出式方法的优点是简单得多。多 UI 线程方法的一个优点是您可以拥有第二个功能齐全的窗口,而不是弹出窗口。

      这两种方法的一个问题是,当您移动主窗口时,您的第二个窗口不会随之移动。话虽这么说,至少对于第二个窗口方法,您的第二个窗口独立于您的第一个窗口比使用弹出方法更清楚。此问题的解决方案包括使用 adorner(如 question 中的建议)或将主窗口的一部分留作“附加信息”,然后您可以通过更改可见性来显示/隐藏图表等内容。

      【讨论】:

      • 切换弹出窗口包含错误;)您也可以将其写成更简单的popup.IsOpen = !popup.IsOpen;
      • 虽然我认为线程示例对于这样的事情相当复杂,最好不要管(因为增加了复杂性,因为 OP 希望从第一个 UI 线程更新其弹出窗口的内容),我同意 xaml 中实际 Popup 的解决方案。
      • @TimothyGroote 我知道你可以做 popup.IsOpen = !popup.IsOpen;但我认为它可能有点微妙而被错过。我同意,几乎可以肯定,在这种情况下,弹出窗口是更好的解决方案,我只是想我会提出一个替代方案。
      【解决方案3】:

      如果您在单独的线程上显示弹出窗口,它不会停止阻止您的主窗口,例如

      public void MessageBoxShow(string Message)
      {
          Thread errorMessageThread = new Thread(() =>
          {
              MessageBox.Show(Message);
          });
          errorMessageThread.IsBackground = true;
          errorMessageThread.Start();
      }
      

      【讨论】:

      • 他想在第二个窗口显示图表,你不能用MessageBox来做到这一点。
      • 我以消息框为例,我没有提供完整的解决方案
      • 我质疑这是多么有用,因为您只是在另一个线程上调用 show 方法。另外,这是关于messagebox,与op的问题无关。澄清一下,我没有尝试过,但由于 WPF 使用了一个主“ui 线程”,因此最终结果可能只是多一点开销而没有实际收益。我什至认为这可能会由于尝试跨线程访问而导致运行时错误
      • 是消息框不是弹出的形式???我可以向您保证没有运行时错误。但感谢您的反对
      • @stuicidle 是的,消息框是一种弹出形式,但您不能使用自定义控件填充它。投反对票的原因是建议无偿使用线程而不了解所获得的好处和您为此付出的代价。如果您要从另一个线程启动弹出窗口,则默认绑定模型会因该弹出窗口而中断,因为模型是从另一个线程(在主窗口的线程中)更新的。对视图模型的所有属性更改都需要调度程序调用,即使使用数据绑定也是如此。