WPF中SplashScreen不显示及主窗口加载失败问题求助
初始问题描述
我通过以下方式创建了SplashScreen:新建一个包含不确定进度条(用于显示加载状态)和标签的窗口,为该窗口添加代码:
ResizeMode="NoResize" WindowStyle="None" WindowStartupLocation="CenterScreen" Background="Blue" BorderThickness="5" BorderBrush="AliceBlue";随后在App.xaml.cs的OnStartup中编写代码:SplashScreen splash = new SplashScreen(); splash.Show(); MainWindow main = new MainWindow(); for (int i = 0; i < 100; i++) { Thread.Sleep(i); } splash.Close(); main.Show();现在出现的问题是SplashScreen不显示,应用似乎处于繁忙状态,请问原因是什么?
更新后的问题
我按照建议编写了如下代码:
private void _applicationInitialize(Views.SplashScreen splashWindow) { var dispatcherTimer = new DispatcherTimer(); dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick); dispatcherTimer.Interval = new TimeSpan(0, 0, 1); dispatcherTimer.Start(); //Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Invoker)delegate //{ // MainWindow = new MainWindow(); // MainWindow.Show(); //}); } private void dispatcherTimer_Tick(object sender, EventArgs e) { MainWindow = new MainWindow(); MainWindow.Show(); }但主窗口仍无法显示。
问题分析与解决方案
初始问题的根源
这个问题是WPF开发里很常见的坑——你把耗时操作直接塞在了UI线程里。WPF的UI线程是单线程模型,所有界面渲染、交互的任务都得靠它处理。你调用splash.Show()后,只是给系统发了一个显示窗口的请求,但实际的绘制、渲染工作还没来得及执行,紧接着就进入了带Thread.Sleep(i)的循环,直接把UI线程堵死了。
UI线程被阻塞时,根本没法处理启动屏的渲染任务,所以你看不到启动屏,同时整个应用因为UI线程卡住,就会显得“繁忙”无响应。
修复方案
把耗时的初始化操作放到后台线程执行,别占着UI线程。用Task.Run跑后台任务,任务完成后再切回UI线程处理窗口的关闭和显示:
protected override void OnStartup(StartupEventArgs e) { var splash = new SplashScreen(); splash.Show(); // 后台线程执行耗时操作 Task.Run(() => { // 这里替换成你的实际初始化逻辑,下面是模拟代码 for (int i = 0; i < 100; i++) { Thread.Sleep(i); } // 必须回到UI线程操作窗口 Dispatcher.Invoke(() => { splash.Close(); var mainWindow = new MainWindow(); mainWindow.Show(); }); }); base.OnStartup(e); }
更新后代码的问题
你用DispatcherTimer的思路是对的,但代码里有几个小疏漏:
- 没有关闭启动屏:主窗口创建后,启动屏可能还停在前台,直接把主窗口挡住了;
- 定时器没有停止:默认情况下定时器会每隔1秒触发一次,反复创建主窗口显然不合理;
- 没有显式绑定UI线程调度器(虽然大部分场景没问题,但显式指定更稳妥)。
修复后的代码
先调整_applicationInitialize方法:
private void _applicationInitialize(Views.SplashScreen splashWindow) { // 显式绑定到当前UI线程的调度器 var dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal, Dispatcher.CurrentDispatcher); dispatcherTimer.Tick += (sender, e) => { dispatcherTimer.Stop(); // 触发一次就停止定时器 splashWindow.Close(); // 先关闭启动屏 MainWindow = new MainWindow(); MainWindow.Show(); }; dispatcherTimer.Interval = TimeSpan.FromSeconds(1); dispatcherTimer.Start(); }
然后确保在OnStartup里正确调用这个方法:
protected override void OnStartup(StartupEventArgs e) { var splash = new Views.SplashScreen(); splash.Show(); _applicationInitialize(splash); base.OnStartup(e); }
内容的提问来源于stack exchange,提问作者il santino




