How to create animated splash screen using Custom application startup class In WPF
Application speaks your business.
The application works fine – check, it does not crash while you pop some popcorns – check, it does show correct results and data – check, it cannot get anymore faster than this – check, does not keep showing “loading, please wait” message for next 100 years – check. Phew, finally your application is all good to go.
But are we not forgetting something?
Presentation or the visuals plays an equally important role in success of any business solution / application. Condition – it is not some high end, supa dupa background processing application. This presentation part can be different for each and every solutions coded out there. They may have glassy sprites and rich menus, some might be theme based.
Some impress well using javascript or animation and so on. One of the Titanic times solution for desktop application to improve presentation quality is start-up splash screen.
Application splash screen can be very handy even to execute some important checks, initial component loading or perform some start-up routine while the user is getting amazed by some “Avatar” animated splash screen window getting rendered on screen.
Lately I started getting in touch with WPF (Dub Pee Eff, as I pronounce) for few research and some side kick development fun. I decided to create a beautiful splash screen for this demo application I was trying my hands on using WPF.
Application start-up, splash screen huh?
You would poke me, saying - Hey Mr Smarty pants, I don't buy that. F.Y.I Microsoft does have built-in support for adding splash screen in WPF. Now end this jibbar jabber and Google it up.
Well, I did already. Built-in splash screen support in WPF is actually making an image appear as splash screen when your application starts. You can do that by adding an image to your WPF project and from properties change its build action from resource or whatever to SplashScreen.
Poking back - It do not support animated images or motion images like GIFs, you cannot add any animation to or over that image, cannot control anything related to that splash screen image via any XAML or C# code and also cannot do any fun thing (designing stuffs is fun) from Blend.
Aye, stop pulling your hairs -
Where there is a will (not smith), there is a way !!
There are many work arounds to overcome the above limitation (I say that). I got myself stuck with one, where we can use a custom application start-up class and do the splash screen loading first and then start the normal application loading as it does in traditional way.
This would give us limitless control on what we can do as splash screen on front and what we can process behind the scence.
Now, I googled and I came across a good but incomplete article which demonstrated the way to do this.
I tried to create a demo as directed by the author and tried to run it. It did executed successfully. But as soon as I tried to add some splashy animation (storyboard), and some background work and the application started fumbling (dying like a mad cow). The article was incomplete and I say because it did not served the goal “completely”. Anyways, so I tried to complete what was missing.
Fire – “devenv” (my style) > Create a new “WPF Application” (I named it – CustomStartupSplashScreenDemo).
Now as we all are familiar from the time of Windows forms application. There is a static Main
method in every desktop application (including console). There is always one and only one static Main method in an application and that is the main startup entry point of the application.
A WPF application is not an exception. You will not find a static entry point in a WPF application, because .NET creates it for you in your application. Navigate to folder – obj > x86 > Debug > TempPE > App.g.cs
to see the auto-generated entry point method.
The first thing you need to change to create your custom splash screen is to take control of your main method. To do so, right click on the App.xaml and click on properties.
You will notice that Build Action
is set to ApplicationDefinition
. This setting actually indicates .NET compiler to create that static main in your application. Change the Build Action
of App.xaml to Page
.
Next step would be adding a new class to your application, call it “ApplicationStartup.cs”. Then add a static Main method to this class
[STAThreadAttribute()]
public static void Main()
{
}
The splash screen window will run on different thread so let us create an interface to talk to and fro with the splash screen window. Create an interface “ICustomSplashScreen.cs” and define two method into that:
void AppLoadingPercentCompleted(int currentPercentage);
void AppLoadingCompleted();
you can add more methods to the interface as per your requirement.
Next add a new WPF window item to your project, which we will make our splash screen. Design it well adding proper storyboards, introductory text and flashy animation. Splash screen name – “DemoSplashScreen”.
I changed some UI setting to make it look more like a traditional splash screen – ShowInTaskbar (false), ResizeMode (NoResize), WindowStartupLocation (CenterScreen), WindowStyle (None) etc. Implement ICustomSplashScreen
to this above created window and add your custom logic to do crazy thing you wish too.
Below code show the content of ApplicationStartup.cs
public sealed class ApplicationStartup
{
#region Variable Declaration
/// <summary>
/// Thread that runs the application splash screen on application startup.
/// </summary>
private static Thread applicationSplashScreenThread = null;
/// <summary>
/// Instance of application splash screen window.
/// </summary>
private static DemoSplashScreen applicationSplashScreen = null;
/// <summary>
/// Manual reset event instance to hold the application splash screen thread till splash screen completes its loading.
/// </summary>
private static ManualResetEvent manualResetEventSplashScreen = null;
#endregion
#region Constructors
/// <summary>
/// Main entry point method for Demo application.
/// </summary>
[STAThreadAttribute()]
public static void Main()
{
manualResetEventSplashScreen = new ManualResetEvent(false);
applicationSplashScreenThread = new
Thread(DisplayApplicationSplashScreen);
applicationSplashScreenThread.SetApartmentState(ApartmentState.STA);
applicationSplashScreenThread.IsBackground = true;
applicationSplashScreenThread.Name = "Demo Splash Screen Thread";
applicationSplashScreenThread.Start();
manualResetEventSplashScreen.WaitOne();
var application = new App(applicationSplashScreen);
application.InitializeComponent();
application.Run();
}
#endregion
#region Static Methods
/// <summary>
/// Display application splash screen.
/// </summary>
private static void DisplayApplicationSplashScreen()
{
applicationSplashScreen = new DemoSplashScreen();
applicationSplashScreen.Show();
manualResetEventSplashScreen.Set();
Dispatcher.Run();
}
#endregion
}
In main method we have started a new thread that runs the application splash screen window by setting the manual reset event and calling dispatcher thread to run. Once we set the reset event we start the application passing the instance of DemoSplashScreen window so that we can control the same from the application App.xaml.cs
to do background work while application is showing splash screen and close the same when required background work is completed.
Below code show the content of App.xaml.cs
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
/// <summary>
/// Instance of DemoSplashScreen window.
/// </summary>
private DemoSplashScreen appSplashScreen = null;
/// <summary>
/// Application splash screen loading timer.
/// </summary>
private static ST.Timer applicationSplashScreenTimer = null;
/// <summary>
/// Application loading percentage counter.
/// </summary>
private static int loadingCount = 0;
/// <summary>
/// Initializes a new instance of the App class.
/// </summary>
/// <param name="appSplashScreen">Instance of application splash screen window.</param>
public App(DemoSplashScreen appSplashScreen)
{
this.appSplashScreen = appSplashScreen;
applicationSplashScreenTimer = new ST.Timer(1000);
applicationSplashScreenTimer.Elapsed += new ST.ElapsedEventHandler(applicationSplashScreenTimer_Elapsed);
applicationSplashScreenTimer.Start();
}
/// <summary>
/// Event handler for Application splash screen timer time elapsed event.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="eventArgs">Event arguments.</param>
private void applicationSplashScreenTimer_Elapsed(object sender, ST.ElapsedEventArgs eventArgs)
{
if (loadingCount >= 100)
{
applicationSplashScreenTimer.Stop();
applicationSplashScreenTimer.Dispose();
applicationSplashScreenTimer = null;
appSplashScreen.AppLoadingCompleted();
appSplashScreen = null;
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(StartMainApplication));
}
else
{
loadingCount += 1;
appSplashScreen.AppLoadingPercentCompleted(loadingCount);
}
}
/// <summary>
/// Start demo application main window.
/// </summary>
private void StartMainApplication()
{
this.MainWindow = new MainWindow();
this.MainWindow.Show();
}
}
Following code is mostly self-explanatory. The only special thing we do here is to call appSplashScreen.AppLoadingCompleted() when the work is done. Then we call begin invoke on dispatcher before it stops, to start our application’s main window. There are few other ways to do so, but I prefer the simple one.
This code is from DemoSplashScreen.xaml.cs
/// <summary>
/// Interaction logic for DemoSplashScreen.xaml
/// </summary>
public partial class DemoSplashScreen : Window, ICustomSplashScreen
{
/// <summary>
/// Initializes a new instance of the DemoSplashScreen class.
/// </summary>
public DemoSplashScreen()
{
InitializeComponent();
}
/// <summary>
/// Application loading percentage completed process.
/// </summary>
/// <param name="currentPercentage">Current application loaded percentage</param>
public void AppLoadingPercentCompleted(int currentPercentage)
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action<string>(SetLoadingStatus), currentPercentage.ToString());
}
/// <summary>
/// Application loading completed process.
/// </summary>
public void AppLoadingCompleted()
{
Dispatcher.InvokeShutdown();
}
/// <summary>
/// UI dispatcher thread execution process to update the controls state.
/// </summary>
/// <param name="valueLoading">current value of application loaded.</param>
private void SetLoadingStatus(string valueLoading)
{
this.txtBoxApplicationLoadCompletePercentVal.Text = valueLoading;
}
}
Here the code I would talk about is Dispatcher.InvokeShudown which will terminate the splash screen window and its thread and return the control to the dispatcher thread where our App has started running. Also not forgot to mention the Dispatcher.Invoke to method where we can take to controls on the UI thread of DemoSplashScreen window in thread safe way.
Sample code on Github for How to create animated splash screen using Custom application startup class In WPF
Happy Coding!!