给软件增加一个启动屏幕来提升启动体验

零、版本

日期备注
2020.05.01初稿
2020.05.02调整章节
2020.05.03增加尾声

一、什么是启动屏幕

常见的大型软件,如Word、Excel、Photoshop、Visual Studio等等,由于启动时要加载很多模块,为了提高界面响应,都会在启动时会先打开一个小窗口,等到主界面初始化完成之后再关闭。

就像下面这个样子:

MsWord启动屏幕

通常我们把这个显示启动进度的小界面称为启动屏幕或者闪屏,英文大概就叫Splash

二、使用启动屏幕的好处

在软件中集成启动屏幕好处有二:

其一:通过适当的反馈可以提升用户体验

用户双击某个程序后,通过启动屏幕可以知道,程序打开了,正在启动中,需要耐心等待一会儿。

试想,如果用户双击了一个文件,等待5秒钟后还没打开,他的第一反应会不会以为刚刚没点到,再去打开一遍呢?其实进程已经启动了,只是界面还没加载出来,这样的体验是很糟糕的。

其二:彰显品牌概念

在程序启动过程中显示Logo及程序名称,可以彰显软件品牌以及定位。

启动屏幕上的内容不是随意设置的,一般都会突出显示软件的名称和加载进度。

三、定义SplashWindow

由于工作原因,我经手的项目中也会碰到这种场景。程序启动时需要读取数据库,加载配置,还可能需要联网校验授权码,这些操作都是相当耗时的。

下面以WPF项目为例,说明如何制作一个自定义的闪屏界面。

首先新建一个Window,这里命名为SplashWindow,由于项目中用到了一个老外的开源控件包Mahapps.Metro,这里就毫不吝啬地用了它的ProgressBar,经过一番排版之后,一个像样的启动界面就好了。

SplashWindow布局

这个布局是不是似曾相识?没错,界面布局就是借鉴了微软的Word2016。

什么?你说这是抄袭?读书人怎么能用“抄袭”二字呢?这叫借鉴,借鉴。。。

XAML布局代码如下。

<Window x:Class="SetDataCo.Views.SplashWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:local="clr-namespace:SetDataCo.Views"
        mc:Ignorable="d"
        Title="{Binding AppName, Mode=OneWay, Source={StaticResource appConst}}"
        Icon="{StaticResource appIcon}"
        Height="300" Width="550" WindowStartupLocation="CenterScreen" 
        ResizeMode="NoResize"
        Background="{StaticResource AccentBaseColorBrush}"
        WindowStyle="None">

    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.RowSpan="2">
            <TextBlock Text="Data Cooperating System"
                       FontStyle="Italic"
                       FontWeight="Bold"
                       Foreground="White" />
            <Image Source="/SetDataCo;component/Resources/Images/AppLogo.png"
                   Height="64"
                   Margin="0,40,0,0" />
            <TextBlock HorizontalAlignment="Center"
                       Text="{Binding AppName, Source={StaticResource appConst}}"
                       Foreground="White"
                       Margin="0,20,0,0"
                       FontSize="30">
            </TextBlock>
            <mahapps:MetroProgressBar IsIndeterminate="True" 
                                      Foreground="White" 
                                      Margin="0,30,0,0"/>
        </StackPanel>

        <TextBlock x:Name="txtMessage" 
                   Text="正在启动..." 
                   Foreground="White" 
                   Grid.Row="1"/>
    </Grid>
</Window>

给这个SplashWindow定义一个实时显示进度的方法,其实就是更新界面上的文本。

public void SetMessage(string message)
{
    this.txtMessage.Text = message;
}

四、调用

接下来就该让这个界面动起来了。在App类中重写OnStartup方法,新开一个线程,用来打开这个SplashWIndow。

SplashWindow splashWindow = null;
Thread t = new Thread(() =>
{
    splashWindow = new SplashWindow();
    splashWindow.ShowDialog();
});
t.SetApartmentState(ApartmentState.STA);// 设置单线程
t.Start();

为了保证主线程往下执行时SplashWindow已经初始化完毕,需要等待一下。

do
{
    Thread.Sleep(50);
} while (splashWindow == null);

只要调用SetMessage方法就可以实时更新进度。

splashWindow.Dispatcher.Invoke((Action)(() => splashWindow.SetMessage("初始化主程序...")));

等后台以及主窗口都加载完成再关闭这个SplashWindow。

splashWindow.Dispatcher.Invoke((Action)(() => splashWindow.Close()));

至此,我们便实现了一个自定义的启动屏幕。

五、完整的启动代码

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    SplashWindow splash = null;
    Thread t = new Thread(() =>
    {
        splash = new SplashWindow();
        splash.ShowDialog();
    });
    t.SetApartmentState(ApartmentState.STA);// 设置单线程
    t.Start();

    do
    {
        Thread.Sleep(50);
    } while (splash == null);

    splash.Dispatcher.Invoke((Action)(() => splash.SetMessage("检测运行环境...")));

    // 这边可以执行一些校验以及初始化操作
    // 加载A模块
    InitModuleA();
    splash.Dispatcher.Invoke((Action)(() => splash.SetMessage("初始化A模块...")));
    
    // 加载B模块
    InitModuleB();
    splash.Dispatcher.Invoke((Action)(() => splash.SetMessage("初始化B模块...")));

    MainWindow mainWindow = new MainWindow();
    Current.MainWindow = mainWindow;

    mainWindow.Show();

    // 在sw的线程上关闭SplashWindow
    if (plash != null)
    {
        splash.Dispatcher.Invoke((Action)(() => splash.Close()));
    }
}

六、尾声

到这里,本文其实就可以结束了。坐在电脑前看这篇文章的你,也许会想,这跟网上看到的有什么区别呢?

其实不然。

按照上面的代码启动后,大部分情况下主窗口都不会再最前方显示,即不是活动窗口,除非设置TopMostTrue。一旦这么做了,窗口将永远置顶,对用户体验是相当不好的。

可能你又想说,等主窗口加载完了再把TopMost改False不就行了。

我也这么尝试过,经过多次试验,还是有一定概率不会是活动窗口。

其实只需要在启动完成后增加下面这行代码,就能解决烦恼。

mainWindow.Activate();

2020年5月1日星期五

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值