如何在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();
});
}
}
}
我假设启动屏幕只存在一次。那么,为什么不让它成为一个单例呢?
最简单的解决方案是通过全局(静态)属性公开它。或者,(如果您创建了一些SplashScreen类),您可以通过MEF或其他组合框架导出和导入它。