延长显示初始屏幕的时间

通过为应用创建扩展的启动画面,使启动画面显示更长时间。 此扩展屏幕模仿启动应用时显示的初始屏幕,但可以自定义。 无论是显示实时加载信息还是只是为应用提供额外的时间来准备其初始 UI,扩展的初始屏幕都允许你定义启动体验。

注释

本主题中的短语“延时启动屏幕”是指停留在屏幕上较长时间的启动屏幕。 这并不表示派生自 SplashScreen 类的子类。

重要 API

本主题使用以下 API:

默认初始屏幕建议

请遵循以下建议,确保您的扩展启动画面准确模仿默认启动画面:

  • 延长的初始屏幕页面应使用 620 x 300 像素的图像,该图像与应用清单中为初始屏幕指定的图像(应用的初始屏幕图像)一致。 在 Microsoft Visual Studio 中,初始屏幕设置存储在应用清单(Package.appxmanifest 文件)的“视觉资产”选项卡的初始屏幕部分。
  • 扩展的启动画面应使用与您的应用程序清单中为启动画面指定的背景色一致的背景色。
  • 你的代码应使用 SplashScreen 类将应用的初始屏幕图像放置在默认初始屏幕所在的同一屏幕坐标处。
  • 代码应通过使用 SplashScreen 类来重新定位扩展启动画面上的项目,以响应窗口大小调整事件(例如,当屏幕旋转或应用程序移动到屏幕上的另一个应用程序旁边时)。

使用以下步骤创建可有效模拟默认启动屏幕的扩展启动屏幕。

空白页 项目添加到现有应用中

本主题假定你想要使用 C#、Visual Basic 或C++将扩展的初始屏幕添加到现有的通用 Windows 平台(UWP)应用项目。

  • 在 Visual Studio 中打开应用。
  • 从菜单栏中按或打开 Project ,然后单击“ 添加新项”。 将显示 “添加新项”对话框。
  • 在这个对话框中,添加一个新的 空白页 到您的应用。 本主题将延长的初始屏幕页面命名为“ExtendedSplash”。

添加 空白页 项将生成两个文件,一个用于标记(ExtendedSplash.xaml),另一个用于代码(ExtendedSplash.xaml.cs)。

扩展启动屏幕的基本 XAML

按照以下步骤将图像和进度控件添加到扩展的启动画面。

在 ExtendedSplash.xaml 文件中:

  • 更改默认 Grid 元素的背景属性,以匹配在应用清单中为应用的初始屏幕设置的背景色(在 Package.appxmanifest 文件的“视觉资产”部分)。 默认的初始屏幕颜色为浅灰色(十六进制值 #464646)。 请注意,创建新的空白页时,默认情况下会提供此 Grid 元素。 无需使用 网格; 那只是用于构建扩展的启动画面的方便基础。
  • Canvas 元素添加到 网格。 你将使用此 Canvas 来放置你的扩展启动屏幕图像。
  • Image 元素添加到 Canvas。 为扩展初始屏幕使用与默认初始屏幕相同的 620 x 300 像素图像。
  • (可选)添加进度控件,以向用户显示您的应用正在加载。 本主题添加 ProgressRing,而不是确定或不确定 ProgressBar

以下示例演示了 网格,并进行了这些添加和更改。

    <Grid Background="#464646">
        <Canvas>
            <Image x:Name="extendedSplashImage" Source="Assets/SplashScreen.png"/>
            <ProgressRing Name="splashProgressRing" IsActive="True" Width="20" HorizontalAlignment="Center"></ProgressRing>
        </Canvas>
    </Grid>

注释

本示例将 ProgressRing 的宽度设置为 20 像素。 你可以手动将其宽度设置为适用于你的应用的值,但是,控件不会以小于 20 像素的宽度呈现。

扩展启动画面类的基本代码

每当窗口大小(仅限 Windows)或方向发生变化时,你的启动画面都需要做出相应调整。 必须更新你使用的图像的位置,以便无论窗口如何更改,延长的初始屏幕看起来都不错。

使用以下步骤设定方法以正确显示扩展的启动画面。

  1. 添加所需的命名空间

    需要将以下命名空间添加到 ExtendedSplash.xaml.cs 才能访问 SplashScreen 类、 Rect 结构以及 Window.SizeChanged 事件。

    using Windows.ApplicationModel.Activation;
    using Windows.Foundation;
    using Windows.UI.Core;
    
  2. 创建分部类并声明类变量

    在ExtendedSplash.xaml.cs中包含以下代码,以创建部分类来表示扩展的启动画面。

    partial class ExtendedSplash : Page
    {
        internal Rect splashImageRect; // Rect to store splash screen image coordinates.
        private SplashScreen splash; // Variable to hold the splash screen object.
        internal bool dismissed = false; // Variable to track splash screen dismissal status.
        internal Frame rootFrame;
    
       // Define methods and constructor
    }
    

    这些类变量由多个方法使用。 该 splashImageRect 变量存储系统显示应用的初始屏幕图像的坐标。 splash 变量存储 SplashScreen 对象,dismissed 变量跟踪系统显示的启动画面是否已关闭。

  3. 为正确定位图像的类定义构造函数

    以下代码为扩展的初始屏幕类定义构造函数,该类侦听窗口大小调整事件、将图像和(可选)进度控件定位在延长的初始屏幕上,创建导航框架,并调用异步方法来还原已保存的会话状态。

    public ExtendedSplash(SplashScreen splashscreen, bool loadState)
    {
        InitializeComponent();
    
        // Listen for window resize events to reposition the extended splash screen image accordingly.
        // This ensures that the extended splash screen formats properly in response to window resizing.
        Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);
    
        splash = splashscreen;
        if (splash != null)
        {
            // Register an event handler to be executed when the splash screen has been dismissed.
            splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(DismissedEventHandler);
    
            // Retrieve the window coordinates of the splash screen image.
            splashImageRect = splash.ImageLocation;
            PositionImage();
    
            // If applicable, include a method for positioning a progress control.
            PositionRing();
        }
    
        // Create a Frame to act as the navigation context
        rootFrame = new Frame();            
    }
    

    请确保在类构造函数中注册 Window.SizeChanged 处理程序(ExtendedSplash_OnResize),以便应用在扩展启动画面中正确定位图像。

  4. 定义一个类方法,将图像定位在扩展的启动屏幕中

    此代码演示如何使用 splashImageRect 类变量将图像放置在延长的初始屏幕页上。

    void PositionImage()
    {
        extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
        extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
        extendedSplashImage.Height = splashImageRect.Height;
        extendedSplashImage.Width = splashImageRect.Width;
    }
    
  5. (可选)定义类方法,以在扩展启动画面中定位进度控件

    如果选择将 ProgressRing 添加到扩展的启动画面,请将其相对于启动画面图像进行定位。 将以下代码添加到 ExtendedSplash.xaml.cs 以在图像下方 32 像素处居中 ProgressRing

    void PositionRing()
    {
        splashProgressRing.SetValue(Canvas.LeftProperty, splashImageRect.X + (splashImageRect.Width*0.5) - (splashProgressRing.Width*0.5));
        splashProgressRing.SetValue(Canvas.TopProperty, (splashImageRect.Y + splashImageRect.Height + splashImageRect.Height*0.1));
    }
    
  6. 在类中定义一个用于处理关闭事件的处理程序

    在 ExtendedSplash.xaml.cs 中,当 SplashScreen.Dismissed 事件发生时,通过将 dismissed 类变量设置为 true 来响应。 如果你的应用程序具有初始化操作,请将其添加到此事件处理程序中。

    // Include code to be executed when the system has transitioned from the splash screen to the extended splash screen (application's first view).
    void DismissedEventHandler(SplashScreen sender, object e)
    {
        dismissed = true;
    
        // Complete app setup operations here...
    }
    

    应用设置完成后,请退出扩展启动画面。 以下代码定义了一个名为 DismissExtendedSplash 的方法,该方法将导航到在您的应用 MainPage.xaml 文件中定义的 MainPage

    async void DismissExtendedSplash()
      {
         await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,() =>            {
              rootFrame = new Frame();
              rootFrame.Content = new MainPage(); Window.Current.Content = rootFrame;
            });
      }
    
  7. 类中,定义 Window.SizeChanged 事件的处理程序

    准备扩展启动屏幕,以便在用户调整窗口大小时调整其元素。 当 Window.SizeChanged 事件发生时,此代码通过捕获新的坐标并重新定位图像来响应。 如果向扩展启动屏幕添加了进度控件,请在此事件处理程序中重新定位它。

    void ExtendedSplash_OnResize(Object sender, WindowSizeChangedEventArgs e)
    {
        // Safely update the extended splash screen image coordinates. This function will be executed when a user resizes the window.
        if (splash != null)
        {
            // Update the coordinates of the splash screen image.
            splashImageRect = splash.ImageLocation;
            PositionImage();
    
            // If applicable, include a method for positioning a progress control.
            // PositionRing();
        }
    }
    

    注释

     在尝试获取图像位置之前,请确保类变量 (splash) 包含有效的 SplashScreen 对象,如示例中所示。

     

  8. (可选)添加类方法以还原保存的会话状态

    在步骤 4 中添加到 OnLaunched 方法的代码:修改启动激活处理程序 会导致应用在启动时显示延长的初始屏幕。 若要在扩展的初始屏幕类中合并与应用启动相关的所有方法,可以考虑将方法添加到ExtendedSplash.xaml.cs文件以还原应用的状态。

    void RestoreState(bool loadState)
    {
        if (loadState)
        {
             // code to load your app's state here
        }
    }
    

    在你修改 App.xaml.cs 中的启动激活处理程序时,如果应用程序先前的 loadstateTerminated,则你还将 设置为 true。 如果是这样,该方法 RestoreState 会将应用还原到其以前的状态。 有关应用启动、暂停和终止的概述,请参阅 应用生命周期

修改启动激活处理程序

启动应用后,系统会将初始屏幕信息传递给应用的启动激活事件处理程序。 可以使用此信息正确定位图像到您的扩展启动屏。 可以从传递给应用的 OnLaunched 处理程序的激活事件参数获取此初始屏幕信息(请参阅 args 以下代码中的变量)。

如果尚未替代应用的 OnLaunched 处理程序,请参阅 应用生命周期 了解如何处理激活事件。

在App.xaml.cs中,添加以下代码以创建并显示延长的初始屏幕。

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    if (args.PreviousExecutionState != ApplicationExecutionState.Running)
    {
        bool loadState = (args.PreviousExecutionState == ApplicationExecutionState.Terminated);
        ExtendedSplash extendedSplash = new ExtendedSplash(args.SplashScreen, loadState);
        Window.Current.Content = extendedSplash;
    }

    Window.Current.Activate();
}

完整代码

以下代码与前面步骤中显示的代码片段略有不同。

  • ExtendedSplash.xaml 包含 DismissSplash 按钮。 单击此按钮时,事件处理程序 DismissSplashButton_Click将调用该方法 DismissExtendedSplash 。 在应用中,在应用完成加载资源或初始化其 UI 时调用 DismissExtendedSplash
  • 此应用还使用 UWP 应用项目模板,该模板使用 框架 导航。 因此,在App.xaml.cs中,启动激活处理程序(OnLaunched)定义 rootFrame 并使用它来设置应用窗口的内容。

ExtendedSplash.xaml

此示例包含一个 DismissSplash 按钮,因为它没有要加载的应用资源。 在你的应用中,当你的应用完成加载资源或准备其初始界面时,会自动隐藏扩展启动画面。

<Page
    x:Class="SplashScreenExample.ExtendedSplash"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SplashScreenExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="#464646">
        <Canvas>
            <Image x:Name="extendedSplashImage" Source="Assets/SplashScreen.png"/>
            <ProgressRing Name="splashProgressRing" IsActive="True" Width="20" HorizontalAlignment="Center"/>
        </Canvas>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom">
            <Button x:Name="DismissSplash" Content="Dismiss extended splash screen" HorizontalAlignment="Center" Click="DismissSplashButton_Click" />
        </StackPanel>
    </Grid>
</Page>

ExtendedSplash.xaml.cs

请注意,方法DismissExtendedSplash是从按钮DismissSplash的单击事件处理程序调用的。 在你的应用中,不需要按钮 DismissSplash 。 相反,当你的应用完成加载资源并想要导航到其主页时调用 DismissExtendedSplash

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

using Windows.ApplicationModel.Activation;
using SplashScreenExample.Common;
using Windows.UI.Core;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234238

namespace SplashScreenExample
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    partial class ExtendedSplash : Page
    {
        internal Rect splashImageRect; // Rect to store splash screen image coordinates.
        private SplashScreen splash; // Variable to hold the splash screen object.
        internal bool dismissed = false; // Variable to track splash screen dismissal status.
        internal Frame rootFrame;

        public ExtendedSplash(SplashScreen splashscreen, bool loadState)
        {
            InitializeComponent();

            // Listen for window resize events to reposition the extended splash screen image accordingly.
            // This is important to ensure that the extended splash screen is formatted properly in response to snapping, unsnapping, rotation, etc...
            Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);

            splash = splashscreen;

            if (splash != null)
            {
                // Register an event handler to be executed when the splash screen has been dismissed.
                splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(DismissedEventHandler);

                // Retrieve the window coordinates of the splash screen image.
                splashImageRect = splash.ImageLocation;
                PositionImage();

                // Optional: Add a progress ring to your splash screen to show users that content is loading
                PositionRing();
            }

            // Create a Frame to act as the navigation context
            rootFrame = new Frame();

            // Restore the saved session state if necessary
            RestoreState(loadState);
        }

        void RestoreState(bool loadState)
        {
            if (loadState)
            {
                // TODO: write code to load state
            }
        }

        // Position the extended splash screen image in the same ___location as the system splash screen image.
        void PositionImage()
        {
            extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
            extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
            extendedSplashImage.Height = splashImageRect.Height;
            extendedSplashImage.Width = splashImageRect.Width;

        }

        void PositionRing()
        {
            splashProgressRing.SetValue(Canvas.LeftProperty, splashImageRect.X + (splashImageRect.Width*0.5) - (splashProgressRing.Width*0.5));
            splashProgressRing.SetValue(Canvas.TopProperty, (splashImageRect.Y + splashImageRect.Height + splashImageRect.Height*0.1));
        }

        void ExtendedSplash_OnResize(Object sender, WindowSizeChangedEventArgs e)
        {
            // Safely update the extended splash screen image coordinates. This function will be fired in response to snapping, unsnapping, rotation, etc...
            if (splash != null)
            {
                // Update the coordinates of the splash screen image.
                splashImageRect = splash.ImageLocation;
                PositionImage();
                PositionRing();
            }
        }

        // Include code to be executed when the system has transitioned from the splash screen to the extended splash screen (application's first view).
        void DismissedEventHandler(SplashScreen sender, object e)
        {
            dismissed = true;

            // Complete app setup operations here...
        }

        void DismissExtendedSplash()
        {
            // Navigate to mainpage
            rootFrame.Navigate(typeof(MainPage));
            // Place the frame in the current Window
            Window.Current.Content = rootFrame;
        }

        void DismissSplashButton_Click(object sender, RoutedEventArgs e)
        {
            DismissExtendedSplash();
        }
    }
}

App.xaml.cs

此项目是在 Visual Studio 中使用 UWP 应用空白应用(XAML) 项目模板创建的。 OnNavigationFailedOnSuspending 事件处理程序是自动生成的,无需更改便可实现扩展启动屏幕。 本主题仅修改 OnLaunched

如果您的应用程序没有使用项目模板,请参阅步骤 4:修改启动激活处理程序,以查看未使用 OnLaunched 导航的修改示例

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Application template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234227

namespace SplashScreenExample
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(
            Microsoft.ApplicationInsights.WindowsCollectors.Metadata |
            Microsoft.ApplicationInsights.WindowsCollectors.Session);
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif

            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();
                // Set the default language
                rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

                rootFrame.NavigationFailed += OnNavigationFailed;

                //  Display an extended splash screen if app was not previously running.
                if (e.PreviousExecutionState != ApplicationExecutionState.Running)
                {
                    bool loadState = (e.PreviousExecutionState == ApplicationExecutionState.Terminated);
                    ExtendedSplash extendedSplash = new ExtendedSplash(e.SplashScreen, loadState);
                    rootFrame.Content = extendedSplash;
                    Window.Current.Content = rootFrame;
                }
            }

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                rootFrame.Navigate(typeof(MainPage), e.Arguments);
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when Navigation to a certain page fails
        /// </summary>
        /// <param name="sender">The Frame which failed navigation</param>
        /// <param name="e">Details about the navigation failure</param>
        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            // TODO: Save application state and stop any background activity
            deferral.Complete();
        }
    }
}

引用