次の方法で共有


Implementing an extended splash screen

[This post is a part of a series of post about the Social Media Dashboard Sample. This post was written by Peter Bryntesson. For an introductory blog post, click here]

When you start a Windows 8 app, the first thing that happens is that the application’s splash screen is displayed at the center of the screen. After it has been displayed, the application is loaded, initialized and navigation to the main page is performed. One of the technical requirements for a Windows 8 and Windows Phone application is that it must display the first page inside of 5 seconds.

Now, that might not be possible in all cases, and the Social Media Dashboard is one such example. The main page consist of results from a number of queries to external services such as YouTube, Flickr, Twitter, blog feeds etc. There is no way of guaranteeing that to happen in 5 seconds.

The solution to the problem is to display an extended splash screen during load of the data and after the data has been loaded navigate to the main page. An extended splash screen should continue to display the same splash screen image plus information about the loading progress. That could be as simple as displaying a progress indicator and a text that says loading, or something more advanced. The Social Media Dashboard uses this basic method.

The implementation is similar in both Windows 8 and Windows Phone. Let’s look at the implementation on Windows 8 first.

The first thing you have to do is to navigate to the extended splash screen instead of the main page. The code for this is in app.xaml.cs, in the function OnLaunched() :

     if (rootFrame.Content == null)
     {
         if (args.PreviousExecutionState != 
             ApplicationExecutionState.Running)
         {
             bool loadState = (args.PreviousExecutionState == 
                 ApplicationExecutionState.Terminated);
             ExtendedSplash extendedSplash = 
                 new ExtendedSplash(args.SplashScreen, 
                     loadState, null);
             Window.Current.Content = extendedSplash;
         }
     }
     // Ensure the current window is active
     //Window.Current.Content = rootFrame;
     Window.Current.Activate();
 

What we do here is to make sure we are starting the app for the first time and not reactivating it. If so, we create the extended splash page and sets that as the current content for the root window. That way it won’t be inserted into the page stack. The SMD Windows 8 app supports the search contract. If activated by search we will be started from the function OnSearchActivated() . Here is the similar code for that:

     bool loadState = (args.PreviousExecutionState == 
         ApplicationExecutionState.Terminated);
     ExtendedSplash extendedSplash = 
         new ExtendedSplash(args.SplashScreen, 
             loadState, args.QueryText);
     Window.Current.Content = extendedSplash;

Very similar code, notice the third parameter, which is the search query. We need to pass that on when we navigate from the extended splash screen later.

Looking at ExtendedSplash.xaml the markup for defining the page looks like this:

     <Grid.RowDefinitions>
         <RowDefinition/>
         <RowDefinition Height="180"/>
     </Grid.RowDefinitions>
  
     <Canvas Grid.Row="0">
         <Image x:Name="extendedSplashImage" 
                Source="../Assets/SplashScreen.png" />
     </Canvas>
     <StackPanel Grid.Row="1" HorizontalAlignment="Center">
         <TextBlock Style="{StaticResource BasicTextStyle}" 
                    Text="{Binding Title}" 
                    FontFamily="{StaticResource AppFontFamily}" 
                    Foreground="White" TextWrapping="Wrap" 
                    TextAlignment="Center" Padding="5" 
                    HorizontalAlignment="Center">
             Loading...
         </TextBlock>
         <ProgressRing x:Name="progress" IsActive="True" 
                       Foreground="White"/>
     </StackPanel>

We define a grid with two rows, with the upper row containing the splash screen logo and the lower row with the text and the progress ring indicator. In the code-behind we will actually move the image to the exact place where the original splash screen image is shown. Now the code-behind for this looks like this:

 

     partial class ExtendedSplash
     {
         // Rect to store splash screen image coordinates.
         internal Rect splashImageRect; 
         internal Frame rootFrame;
  
         // Variable to hold the splash screen object.
         private SplashScreen splash; 
  
         public ExtendedSplash(SplashScreen splashscreen, 
             bool loadState, string searchText)
         {
             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)
             {
                 // Retrieve the window coordinates of the 
                 // splash screen image.
                 splashImageRect = splash.ImageLocation;
                 PositionImage();
             }
  
             // Create a Frame to act as the navigation context 
             rootFrame = new Frame();
  
             // Restore the saved session state if necessary
             RestoreStateAsync(loadState, searchText);
  
         }
  
         async void RestoreStateAsync(bool loadState, 
             string searchText)
         {
             try
             {
                 if (loadState)
                     await SuspensionManager.RestoreAsync();
             }
             catch (Exception)
             {
             }
  
             // Normally you should start the time consuming 
             // task asynchronously here and dismiss the 
             // extended splash screen in the completed 
             // handler of that task. 
             if (!HubDataSource.IsDataLoaded)
             {
                 var configfile = 
                     await Package.Current.InstalledLocation.
                     GetFileAsync(
                     "Assets\\Config\\Config.json");
                 var configtext = await FileIO.ReadTextAsync(
                     configfile);
  
                 JObject o = JObject.Parse(configtext);
                 App.configdata = o.ToObject<Config>();
  
                 await HubDataSource.LoadData(App.configdata);
  
                 if (HubDataSource.Current.AllGroups.Count == 0 && 
                     NetworkInformation.
                     GetInternetConnectionProfile() == null)
                 {
                     var messageDialog = 
                         new MessageDialog(
                             "An internet connection is needed");
                     var result = messageDialog.ShowAsync();
                 }
             }
  
             // Navigate to mainpage    
             if (searchText != null)
                 rootFrame.Navigate(typeof(SearchResultsPage), 
                     searchText);
             else
                 rootFrame.Navigate(typeof(MainPage));
             
  
             // Place the frame in the current Window 
             Window.Current.Content = rootFrame;
  
         }
  
         // 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 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();
             }
         }
     }

In the constructor, we connect an event handler for the SizeChanged event, reposition the splash screen to the same place as the original and starts the asynchronous method of loading our content. The function RestoreStateAsync() loads the data and navigates to the first page. If we have search query it will navigate to the search results page, otherwise it will navigate to the main page. PositionImage() is the function that looks at the original splash screen object an positions the image at the same place. Finally, the ExtendedSplash_OnResize() event handler just calls PositionImage() .

Granted, the extended splash screen could be more ambitious but this illustrates the basic concepts.

Looking at the Windows Phone implementation it’s a similar concept but different implementation details. First you don’t have the concept of an system splash screen, Also, you don’t navigate to the first page in code, that entry is in the WMAppManifest.xml, the NavigationPage entry. This points to ExtendedSplashPage.xaml. Here’s the XAML for the extended splash:

     <Grid x:Name="LayoutRoot" Background="White">
         <Border VerticalAlignment="Stretch" 
                 HorizontalAlignment="Stretch">
             <Border.Background>
                 <ImageBrush Stretch="UniformToFill" 
                   ImageSource="/Assets/Background.jpg" 
                   Opacity="1.0"/>
             </Border.Background>
         </Border>
         
         <StackPanel VerticalAlignment="Center" 
                     HorizontalAlignment="Stretch" 
                     Orientation="Vertical">
             <TextBlock Name="TextBoxName" 
                        TextAlignment="Center" 
                        HorizontalAlignment="Center" 
                        Foreground="{StaticResource AppDarkColor}" 
                        Margin="0,0,0,10" TextWrapping="Wrap" 
                        FontSize="50" FontWeight="Normal"/>
             <ProgressBar IsIndeterminate="True" 
                          Foreground="{StaticResource AppDarkColor}" 
                          HorizontalAlignment="Stretch"/>
             <TextBlock Name="TextBoxLoading" 
                        Text="Loading..." 
                        Foreground="{StaticResource AppDarkColor}" 
                        HorizontalAlignment="Center"/>
         </StackPanel>
     </Grid>

Again similar in concept but different in implementation. We have the splash screen centered in the background and show the text and the progress bar on top of that. The code behind for this is here:

 

     public partial class ExtendedSplashPage : 
         PhoneApplicationPage
     {
         protected async override void OnNavigatedTo(
             NavigationEventArgs e)
         {
             if (!(App.ViewModel as MainPageViewModel).
                 IsDataLoaded)
             {
                 MainPageViewModel.OnConfigFileRead = () =>
                     {
                         Deployment.Current.Dispatcher.
                             BeginInvoke(() =>
                             {
                                 TextBoxName.Text = 
                                     App.Configuration.appname;
                             });
                     };
                 await (App.ViewModel as MainPageViewModel).
                     LoadViewModelData();
                 MainPageViewModel.OnConfigFileRead = null;
             }
             NavigationService.Navigate(
                 new Uri("/Pages/MainPage.xaml", 
                     UriKind.Relative));
         }
  
         public ExtendedSplashPage()
         {
             InitializeComponent();
             App.ViewModel = new MainPageViewModel();
         }
     }

This is a normal phone page, and Everything is done in OnNavigatedTo() . We load the data and then navigate to the main page.

To summarize things we’ve showed why you should display a splash screen and how straightforward it is to implement both on Windows 8 and on Windows Phone. Good luck implementing this feature in your app.

For more information about Windows 8 app development, go here.

For more information about Windows Phone development, go here.