【问题标题】:How to find window running with own UI thread in WPF application如何在 WPF 应用程序中找到使用自己的 UI 线程运行的窗口
【发布时间】:2016-03-19 04:11:56
【问题描述】:

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

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

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

有谁知道如何在不将窗口传递给后台工作人员的情况下获取窗口?

以下代码重现了该问题。将所有需要的引用传递给工作人员应该很容易,但这不是我想要在实际应用程序中做的事情。

App.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>

App.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();
              });
           }
        }
     }

【问题讨论】:

    标签: c# wpf multithreading xaml splash-screen


    【解决方案1】:

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

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

    【讨论】:

    • 我认为将它们传递给工作人员会比使飞溅成为静态或单例更好。静态/单例将要求工作人员知道启动屏幕/应用程序,这是我不喜欢的依赖项。 MEF 或 IoC 也可以解决问题。关键是,当 WPF 应用程序有自己的 UI 线程时,我真的不相信它不可能在 WPF 应用程序中找到当前活动窗口......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多