« Only humans allowed ... how unfair! | Main | WPF - The Ghost Cursor »

Distract your WPF application users with a splash screen!

If you've written any WPF applications -- I bet you've had these feelings:

  • Worry: How patient will your users be? The startup time for your application is much longer than you'd like. (Do you hate the start up time for a WPF application as much as me? )
  • Pain: Annoyed by the long PAUSE that happens the first time you start/launch your WPF application.
  • Concern: How many times will the user double-click your application icon before your application shows up? Will they wonder if they actually started the application only to start several copies after a series of double-clicks?
  • Stressed: You've just bought a new computer with the fastest processor, the fastest disk, and more RAM than is rational, and you convinced your financial officer or IT shop (in my case, it might be my wife!) that this computer would be so fast at everything it did that you can't imagine needing a new computer for many many years! Everything else performs awesome -- just these little WPF applications launch so slowly .........

If you've had those or similar feelings, then what you are in need of is a splash screen!

(Seriously, first try to determine if there's ways of loading your application faster: like minimizing the number and size of assemblies that need to be loaded initially; reduce the amount of JITing that must be done -- less code is often better; use a profiler to find hot spots, use a two stage application launch (a mini-loader followed by the real application); and etc. A splash screen at least makes users feel like something is happening. They received a response back from the system -- it acknowledged their actions and is working.)

One of the typical ways of adding a splash screen would be to add a Window to your project and show it before the main window shows -- you'd probably leave the border off, consider using transparency, it would look awesome. That would work. HOWEVER, it will still take too long to show up -- as there's just too much startup needed by a WPF application before that would be made visible (and the fancier it is, the slower it will load). So, the solution is to build something that relies very little on .NET -- so that it can show BEFORE most of the WPF assemblies and .NET assemblies are loaded and needed.

It's not that difficult to do if you're familiar with Windows 32 programming. I'm talking bare to the metal Win32 programming -- not even the thin wrapper than is offered by Windows Forms or any of the associated Drawing assemblies that are commonly used in a WinForm application: that's too much bloat.

So, what does that mean? It means that this splash screen shows up FAST. If your main EXE is small (maybe just a shim for launching the real application), then it will be even faster!

In the attached ZIP file, I've included the assembly you'll need, a demo project, and a project that is used to create the necessary DLL containing the Bitmap. When I said bare to the metal, I meant it. My splash screen component requires a Native Windows 32 bitmap resource to be made available. You can't use a .NET assembly resource as those are just too slow to load.

Also included is a sample Bitmap that I'm sure you'll want to replace:

image

The bitmap can be of any size (although splash screens typically aren't very large... I like to keep them about 400x250 usually).

There are a few differences about a WPF application that uses this component. First -- you can't allow the default project to create the main window for you automatically.

namespace Demo
{
  public class App : System.Windows.Application
  {
    [STAThreadAttribute()]
    public static void Main()
    {
      Splashing splasher = new Splashing("TheSplash.dll", typeof(App).Module, 101);
      splasher.Show();
      App app = new App();
      app.Run(new Window1(splasher));
    }
  }
}

I've removed the App.xaml and default App.xaml.cs files in my project completely and replaced them with a single App.cs file. In this file, I create an instance of the Splasher component, show it, and then Run the application with a new instance of my main window, Window1. I'm passing the instance of the Splasher to Window1 so the splash screen can be hidden when Window1 has shown (or has progressed far enough to warrant closing).

If you have resources in App.xaml, then you might want to move them to a new file(name), and be sure that the Class reference is removed as well from the xml header. Then add code to the Main method above (before the run and after the App instance is created):

    ResourceDictionary dictionary = new ResourceDictionary();
    dictionary.Source = new Uri("/GlobalResources.xaml", UriKind.Relative);
    app.Resources.MergedDictionaries.Add(dictionary);

Of course, adjust this based on your naming conventions and needs.

The parameters to the Splashing class are:

  • Resource file name: The name of the DLL containing the native Win32 resource Bitmap you want to load. It needs to be in the current directory or use a relative path.
  • Module: Used internally - just pass it the module from the Application object -- that's what it really wants. :)
  • Resource ID: Win32 resources are all assigned integer IDs. This number needs to match the resource ID assigned to your splash screen bitmap. VC++ starts at 101 -- in my sample project, it's the first resource, and it's ID is 101.

You'll need to close/hide the splash screen of course. I use the event IsVisibleChanged in the demo as a signal to hide the splash screen:

    void Window1_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
      if (_splasher != null)
      {
        Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.SystemIdle, 
new ArgsDelegate0(delegate() { _splasher.Close(this); _splasher.Dispose(); _splasher = null; })); } }

The Close method optionally takes a Window object -- it pops the Window to the front (otherwise you may find that the application when run outside of Visual Studio will not retain it's z-order properly). It's bizarre when you leave that out!

What I haven't done in the demo is attach to the various global/local exception handlers to make sure the splash screen goes away properly. Nothing more embarrassing than a splash screen covering an exception dialog (not that you should have that happen ... but ....).

I've included in the zip file the VC++ project needed to build the resource DLL. If you don't have VC++ ... hmmm. I'm not sure what you should do since I don't have access easily to a system that only has, for example, Visual C# Express installed, on it to know what alternatives you might have. There may be some free options on the web that are easy enough. All you need to do is replace the embedded bitmap with an image of your own. In my sample project the Bitmap is called Splash.bmp. As I mentioned before, it can be any size you want. The side benefit of the bitmap living in a secondary DLL is that it can be unloaded and not continue to consume resources like .NET assemblies containing a Bitmap resource would typically do -- once loaded, always loaded.

image

Once built, just copy it to your output folder. There's no real need to include it in your solution as it's not changing.

If persuaded sufficiently, I might add the ability to add changeable text to the splash screen. :)

By clicking on this download link and using the component, you agree to the following terms:

Copyright (c) 2007, WiredPrairie.Us

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  • Neither the name of the WiredPrairie.Us nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

If you use it in a project, please write and tell me (coder @ this web site).

I agree, now download it.

Comments or questions, etc. - email me or leave a comment. Enjoy.

Technorati tags: , , , ,

Comments

Fantastic stuff! Only one caveat - any chance of supplying the source code for the WP.Utilities.Splashing dll? Problem is that the dll supplied isn't signed (strong name), and my project is...

Help support my web site by searching and buying through Amazon.com (in assocation with Amazon.com).