如何在WPF应用程序中找到使用自己的UI线程运行的窗口

本文关键字:UI 自己的 线程 运行 窗口 WPF 应用程序 | 更新日期: 2023-09-27 18:26:46

我有一个应用程序,它使用带有自己的Dispatcher的WPF Window作为启动启动启动窗口。该启动屏幕将向用户显示信息,并且是TopMost窗口。我不想把它作为Application.MainWindow,但必须在创建MainWindow之前显示它。。。

在启动期间或应用程序运行期间的任何时候,后台工作人员都可能检测到致命错误,并且需要在终止应用程序之前显示一个消息框。此消息框必须位于任何打开的窗口的前面。

如果在启动窗口可见的情况下发生此错误,则消息框位于启动屏幕后面。使用启动屏幕作为所有者或隐藏启动屏幕可以解决这个问题,但我找不到启动屏幕。它不在Application.Current.Windows集合中,我想是因为它有一个不同的Dispatcher。。。

有人知道如何在不把窗口传给后台工作人员的情况下打开窗口吗?

以下代码再现了此问题。将所有需要的引用传递给工作者可能很容易,但这不是我在实际应用程序中想要做的。

应用程序xaml

  <Application
     x:Class="Sandbox.App"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  </Application>

应用程序.xaml.cs

     using System;
     using System.Linq;
     using System.Threading;
     using System.Windows;
     using System.Windows.Controls;
     using System.Windows.Media;
     using System.Windows.Threading;
     namespace Sandbox
     {
        public partial class App : Application
        {
           protected override void OnStartup(StartupEventArgs e)
           {
              base.OnStartup(e);
              var closeSplashEvent = App.LanuchSplashScreen();
              App.StartThreadInDifferentAssemblyWithoutRefToThis();
              // ...
              this.MainWindow = new Window { Title="MainWindow",  Width = 800, Height = 600, WindowStartupLocation = WindowStartupLocation.CenterScreen };
              this.MainWindow.Show();
              //  ...
              App.CloseSplashScreen(closeSplashEvent, TimeSpan.FromSeconds(5));
           }
           private static void StartThreadInDifferentAssemblyWithoutRefToThis()
           {
              ThreadPool.QueueUserWorkItem(_ =>
              {
                 // ...
                 Thread.Sleep(1000);
                 Application.Current.Dispatcher.Invoke((ThreadStart) delegate
                 {
                    // how to find the splash screen ???
                    var owner = Application.Current.Windows.OfType<Window>().FirstOrDefault(w => w.Title == "SplashScreen") ?? Application.Current.MainWindow;
                    MessageBox.Show(owner, "Should be in front of splash screen!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                 });
              });
           }
           private static ManualResetEvent LanuchSplashScreen()
           {
              Dispatcher splashDispatcher = null;
              var startupEvent = new ManualResetEvent(false);
              var splashThread = new Thread(() =>
              {
                 splashDispatcher = Dispatcher.CurrentDispatcher;
                 startupEvent.Set();
                 Dispatcher.Run();
              });
              splashThread.SetApartmentState(ApartmentState.STA);
              splashThread.Start();
              startupEvent.WaitOne();
              Window aniSplashWindow = null;
              splashDispatcher.Invoke((ThreadStart)delegate
              {
                 aniSplashWindow = new Window
                 {
                    Width = 320, Height = 240, AllowsTransparency = true, Background = Brushes.Transparent, Topmost = true, WindowStyle = WindowStyle.None, WindowStartupLocation = WindowStartupLocation.CenterScreen,
                    Title="SplashScreen", Content = new Border { BorderBrush = Brushes.Black, BorderThickness = new Thickness(1), Background = Brushes.White, CornerRadius = new CornerRadius(5) }
                 };
                 aniSplashWindow.Show();
              });
              var closeSplashEvent = new ManualResetEvent(false);
              ThreadPool.QueueUserWorkItem(_ =>
              {
                 closeSplashEvent.WaitOne();
                 splashDispatcher.Invoke((ThreadStart) delegate
                 {
                    aniSplashWindow.Close();
                    splashDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
                 });
              });
              return closeSplashEvent;
           }
           private static void CloseSplashScreen(EventWaitHandle closeSplashEvent, TimeSpan closeSplashTimeout)
           {
              ThreadPool.QueueUserWorkItem(_ =>
              {
                 Thread.Sleep(closeSplashTimeout);
                 closeSplashEvent.Set();
              });
           }
        }
     }

如何在WPF应用程序中找到使用自己的UI线程运行的窗口

我假设启动屏幕只存在一次。那么,为什么不让它成为一个单例呢?

最简单的解决方案是通过全局(静态)属性公开它。或者,(如果您创建了一些SplashScreen类),您可以通过MEF或其他组合框架导出和导入它。