Dynamic Splash Screens in WPF

by 

| November 14, 2013 | in

There’s a lot going on when an EliteForm PowerTracker starts up. Some of it isn’t our code, but some of it is. Here’s a high-level list of what happens:

  • Load PowerTracker
  • Load environment file
  • Register events
  • Initialize background workers
  • Initialize queues
  • Retrieve saved user options
  • Check internet connectivity
  • Fetch team data and icons from the server or cache
  • Register Coach’s Tablet (if enabled)
  • Set up periodic health checks
  • Initialize cameras
  • Initialize views and view models

The PowerTracker is usually running when an athlete steps up to the rack so they usually never see the splash screen we display when all of the above events occur. But when there is a network timeout or a user initiates the startup, a static image splash screen isn’t the best option. We need some sort of feedback that says we are in the process of starting up so the user doesn’t think that the app has crashed or is hung up.

To come with what we needed I based my technique on this handy post, but it didn’t have an implementation.

You too can build a splash screen for this same kind of implementation. We’ll begin with a WPF project with a static splash screen (starting code) and change it to one with an informative, animated, fully customizable splash screen (ending code).

Let’s start by removing the old splash screen. Select the old splash screen file and go to the Properties. Change the Build Action from “SplashScreen” to “None”.

Select the old splash screen file and go to the Properties. Change the Build Action from “SplashScreen” to “None”.

Next, let’s design our new awesome splash screen. Right-click on the project, select “Add” and “Window”, and name it “AnimatedSplashScreenWindow.xaml” to match the finished solution.

Since it’s just a WPF window you can do anything WPF allows. For now, let’s just add a nice image, some update text that shows what we’re currently working on, and an animated ellipse that shows the program isn’t hanging. Check out the completed code if you need more detail.

Let's just add a nice image, some update text that shows what we're currently working on, and an animated ellipse that shows the program isn't hanging.

There. Looks great!

Now we need to show it. To do this, we’re going to fire up a thread in the OnStartup method in App.xaml.cs. This will allow other app startup tasks to continue while we’re waiting. Let’s take a closer look at the code, it’s a bit tricky. Check out the MSDN documentation on ManualResetEvent to learn more.

[sourcecode language="plain"]

using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Data;
 using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Shapes;

namespace DynamicSplashScreen
 {
 /// <summary>
 /// Interaction logic for SplashScreenWindow.xaml
 /// </summary>
 public partial class AnimatedSplashScreenWindow : Window, ISplashScreen
 {
 public AnimatedSplashScreenWindow()
 {
 InitializeComponent();
 }

public void AddMessage(string message)
 {
 Dispatcher.Invoke((Action)delegate()
 {
 this.UpdateMessageTextBox.Text = message;
 });
 }

public void LoadComplete()
 {
 Dispatcher.InvokeShutdown();
 }
 }

public interface ISplashScreen
 {
 void AddMessage(string message);
 void LoadComplete();
 }
 }

[/sourcecode]

Based on Greg’s post, I’ve defined and implemented most of the ISplashScreen interface in AnimatedSplashScreen.xaml.cs. You can set the update text and tell the window when you are done loading. Remember that any of these UI changes have to be run through the dispatcher. You can’t manipulate UI elements from another thread (for good reason; <a href=”http://msdn.microsoft.com/en-us/library/ms741870.aspx” target=”_blank”>see here</a>).

[sourcecode language="plain"]

using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Data;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Windows;

namespace DynamicSplashScreen
 {
 /// <summary>
 /// Interaction logic for App.xaml
 /// </summary>
 public partial class App : Application
 {
 public static ISplashScreen splashScreen;

private ManualResetEvent ResetSplashCreated;
 private Thread SplashThread;
 protected override void OnStartup(StartupEventArgs e)
 {
 // ManualResetEvent acts as a block. It waits for a signal to be set.
 ResetSplashCreated = new ManualResetEvent(false);

// Create a new thread for the splash screen to run on
 SplashThread = new Thread(ShowSplash);
 SplashThread.SetApartmentState(ApartmentState.STA);
 SplashThread.IsBackground = true;
 SplashThread.Name = "Splash Screen";
 SplashThread.Start();

// Wait for the blocker to be signaled before continuing. This is essentially the same as: while(ResetSplashCreated.NotSet) {}
 ResetSplashCreated.WaitOne();
 base.OnStartup(e);
 }

private void ShowSplash()
 {
 // Create the window
 AnimatedSplashScreenWindow animatedSplashScreenWindow = new AnimatedSplashScreenWindow();
 splashScreen = animatedSplashScreenWindow;

// Show it
 animatedSplashScreenWindow.Show();

// Now that the window is created, allow the rest of the startup to run
 ResetSplashCreated.Set();
 System.Windows.Threading.Dispatcher.Run();
 }
 }
 }

[/sourcecode]

When loading things in our MainWindow constructor or elsewhere, we can just make a call to AddMessage. After we’re done, we can call LoadComplete and the splash screen window will close itself.

dynamic_splash_3

Now let’s test it out. Fire up the program with F5. Notice that the splash screen is loaded before anything else, the text is updating as the app is starting up, and the application doesn’t look like it is hung.

But most importantly, no boring old splash screen!


Related posts