Further Update: there was a bug relating to the splash screen start application not correctly closing. This has now been fixed and you can download it here
Update: Thanks to all of those who have pointed out that it didn't work correctly on windows XP, I have now fixed this and you can download it here
WPF 3.5 Service Pack 1 introduced a new splash screen feature which was supposed to provide a splash screen whilst the program loads. Unfortunately it has a number of faults:
- Although the splash screen class uses native code within it, it is actually part of the .net framework so a large part of the framework has to load before the splash screen can be displayed. This still provides a substantial period of time, particularly on cold boots where the user doesn't have any indication that your application is loading.
- The splash screen will display images stored in resources in either an application or a dll. In my application I need to load the splash screen from an image at runtime. I have submitted a suggestion on connect that this feature should be added.
- There is a bug in the splash screen code, that in certain cases where a user may shift focus away from the splash screen, the splash screen will throw an exception causing the application to crash. I have submitted this issue as a bug on connect, but Microsoft seem to have ignored my attempts to reopen it, so it is anyone's guess it will be fixed in the update to the service pack due early next year
- Because it is not set to be on top, the splash screen can disappear below your application.
The last three issues could have been avoided, had the splash screen been part of the beta of the service pack. It was not available until the final release, so we were unable to comment on these issues or even discover them prior to that.
Recently, Bradley Grainger on the Logos Bible Software Code Blog posted a series on how to build a splash screen in native code. His solution is to build an executable which displays the splash screen using native code and then runs your WPF application. This resolves problem number one because it starts much more quickly than the .net framework and makes it clear to the user what is going on. Of course it also resolves problem number 3 because it doesn't have the same code is .net is using. However it does not address problem number 2, and his code was posted without a compilable version, he just posted snippets of code in his blog.
Since I needed to solve my splash screen problems, I built Bradley’s code into a new Visual C++ application and made a few improvements including being able to display images not in the resources, being always on top, and fading out the splash screen like WPF does when it no longer needs to be displayed.
The source code can be downloaded here
The code is available under the same licence as the original.
To test it for yourself, after you have downloaded the code, run splashscreenstarter.exe in the root folder. If you want to see how it works in a real world application, this video shows you how we use it in our virtual tour viewer.
So, if you wish to use it in your own application, what do you do? You can modify the splashscreenstarter project. Generally you should only need to change this line of code in splashscreenstarter.cpp:
CSplashScreen splash(
hInstance,
// Length of time in milliseconds to display splashscreen fading
3000,
// Specifies the way to load the image for the splashscreen
// To load from the resources, as we have done here, provide the
// resource name and the resource type name. We have used a jpg.
// If you use a PNG, its opacity should be honored to allow you
// to display partially transparent splashscreens.
// To edit the resources in a C++ application go to the resource
// view tab
// To load from a file use this line instead, where filename is the file
// you wish to load:
// new CFileImageLoader(filename),
new CResourceImageLoader(MAKEINTRESOURCE(IDR_SPLASH), _T("JPG")),
// Application prefix. This will be added to the event name so there are no
// conflicts between applications.
_T("SplashScreenStarter"),
// File name of your executable to run. The extension does not need to be .exe.
// If you want to stop your users from starting your application without displaying
// the splashscreen you could use a different extension.
// Is assumed it is in the same folder as this program
// If it is not you can call splash.SetFullPath
_T("SplashScreenTester.exe")
) ;
You will need to replace the image used for the splashscreen which is stored in startuppic.jpg. You'll also want to replace the icon with the application icon you are using.
Then in your WPF application class (app.xaml.cs):
protected override void OnStartup(StartupEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Loaded,
(DispatcherOperationCallback)delegate { CloseSplashScreen(); return null; },
this);
base.OnStartup(e);
}
private void CloseSplashScreen()
{
// signal the native process (that launched us) to close the splash screen
using (var closeSplashEvent = new EventWaitHandle(false,
EventResetMode.ManualReset, "CloseSplashScreenEventSplashScreenStarter"))
{
closeSplashEvent.Set();
}
}
Notes that the event name, CloseSplashScreenEventSplashScreenStarter, includes the prefix from above at the end of it. If you use a different prefix you will need to modify this.
If you wish to close the splash screen at any each time without fading it you can call this code:
static protected void CloseSplashScreenWithoutFade()
{
// signal the native process (that launched us) to close the splash screen
using (var closeSplashEvent = new EventWaitHandle(false,
EventResetMode.ManualReset, "CloseSplashScreenWithoutFadeEventSplashScreenStarter"))
{
closeSplashEvent.Set();
}
}
Hopefully this code will help you to create a more responsive application. If you have any questions or comments, please feel free to contact me.
…Stefan