【发布时间】: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