You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

WPF中SplashScreen不显示及主窗口加载失败问题求助

WPF启动屏与主窗口显示问题排查

初始问题描述

我通过以下方式创建了SplashScreen:新建一个包含不确定进度条(用于显示加载状态)和标签的窗口,为该窗口添加代码:ResizeMode="NoResize" WindowStyle="None" WindowStartupLocation="CenterScreen" Background="Blue" BorderThickness="5" BorderBrush="AliceBlue";随后在App.xaml.csOnStartup中编写代码:

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. 没有关闭启动屏:主窗口创建后,启动屏可能还停在前台,直接把主窗口挡住了;
  2. 定时器没有停止:默认情况下定时器会每隔1秒触发一次,反复创建主窗口显然不合理;
  3. 没有显式绑定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

火山引擎 最新活动