Click here to Skip to main content
Click here to Skip to main content
Go to top

Creating a Splash Screen that runs without a message pump

, 14 Mar 2007
Rate this:
Please Sign up or sign in to vote.
How to create a Splash Screen that can start before the main application thread.

Sample Image - CustomSplashScreen.png

Introduction

Why, you may well ask yourself, does the world need another splash screen control? I have to agree with such sentiment. I myself have found hundreds, if not thousands of examples on the web, many on this site, in every language imaginable.

I have found a running theme in all cases however. While some offer transparency, fading in and out, status update, separate thread execution, even an excellent example using a custom ApplicationContext class, all of these have one thing in common. Most of these splash screens are initiated in the main form load of the application, then closed once the form has finished loading. The rest initiate before the main form, and are closed before the main form execution begins. None of them (that I have found) cover the scenario of initiating before the main Application thread and being disposed after the Main form has fully loaded.

Background

A project I worked on recently included my early attempts at this, using a separate thread and so forth, however, I had a problem. As soon as the Splash Screen closed the main form of the application would go to the back of the z-order behind any other open applications. This was particularly annoying when launching the application from the IDE.

We had, as I often do, good reason to run in this manner as a whole range of classes had to be discovered and dynamically loaded before the Main Application Window could be launched. As a more common requirement we wanted to display the splash screen while authenticating against a web service. If the user could not be automatically authenticated the splash screen would be closed and user credentials requested, or possibly rejected, therefore closing the application without ever going near the main application window.

Explanation of the code

In the source code I have included two projects. One in C# and the other in VB.NET, both compiled on the .NET Framework 2.0 . I tried back porting this control to 1.1 or 1.0, but certain tricks just aren't there in 1.n of the framework. Things like the fade out are not possible without a message loop, at least not in the same manner, so I have left this as a 2.0 framework-only solution. For the purposes of this article I shall present the code examples from the C# source code. The VB.NET code is very similar, and you can find it in the source code solution.

The important part, of either project, is the Splash class. I suggest that the best way to use this is to copy the entire Splash.cs or Splash.vb into your project and work with it there, making the appropriate Namespace changes. This is also recommended as the entire splash screen code and resources will then be loaded as part of your main executable and no external assemblies will be required.

Now for a short list of the main features, and how I implemented them. I found some neat tricks on the way.

Splash Image Transparency

This feature has been done ad nauseam so I'll be brief. You use the forms designer to set the BackgroundImage property of the Splash screen class to the image you wish to display. This should have a transparency colour set; it does not matter what it is. At runtime the Splash screen resizes to fit around your image and sets it's TransparencyKey to it's BackgroundColor, thereby rendering the form transparent, as demonstrated in the source code, and in the picture above.

Splash Fading In

Now the fun begins, as this is a little tricky without a message pump. I have exposed the class as a singleton instance so that you can only show one splash at a time, then we get down to business. But first, a short explanation of what this 'without a message pump' is all about.

At some point in your application you will find the code - Application.Run(new MainForm()), although this is generally hidden from VB.NET programmers as the framework generates this for you at compile time when you set the Startup form for the Application. However, you will find an example of this in VB.NET in the source code. This code not only displays the main form of your application, it also starts a message loop so that Windows can send the application messages to process mouse clicks, keyboard key presses, paint events and so forth. More importantly, it also starts up a message pump to read those messages of the loop. Without this pump the messages would sit on the message loop forever, and your application would be totally unresponsive.

As I mentioned in the introduction, authentication should be occurring before the message pump starts. These are non GUI actions that do not require any direct user interaction. You just want to display the splash screen so the users know something is happening. Technically you are using a message pump, but all interaction with it is manual via the Application.DoEvents() method.

So you call the Show method on the Splash Form, making it visible but not capturing the execution thread. If we had used ShowDialog then .NET would have started a message pump for us. This, however, just displays a black rectangle on the screen. To process the initial paint event you must call Application.DoEvents() as shown below.

//    Hide initially so as to avoid a nasty pre paint flicker
 Instance.Opacity = 0;
 Instance.Show();
 
 //    Process the initial paint events
 Application.DoEvents();

Now we take whatever value has been specified for the fade in time (for example, 500 milliseconds) and generate the time per fade step. I am using a twenty-step fade to keep it smooth up to about four seconds, which should be long enough for anyone. This value is then stored for use later on for the fade out. Passing a value of zero for the fade in time will result in the splash displaying and closing without a fade. A simple loop is now used to increment the Opacity of the Splash screen, using Thread.Sleep, until it is 100% visible. We could not use a timer here because that requires a message pump. Using this method of fade works equally well if called from within the main form of the application should you wish to display it in that manner.

// Perform the fade in
 if (fadeinTime > 0) {
  //    Set the timer interval so that we fade out at the same speed.
  int fadeStep = (int)System.Math.Round((double)fadeinTime/20);
  Instance.fadeTimer.Interval = fadeStep;

  for (int i = 0; i <= fadeinTime; i += fadeStep){
   System.Threading.Thread.Sleep(fadeStep);
   Instance.Opacity += 0.05;
  }
 } else {
  //    Set the timer interval so that we fade out instantly.
  Instance.fadeTimer.Interval = 1;
 }
 Instance.Opacity = 1;

Splash Fading Out

Making the splash screen disappear is a little different from the fade in. For starters, the main application message pump has probably started by this time but it may not have, so we need to check this and act accordingly. If the application message pump has been started (by a call to Application.Run) then the request to fade out will be coming from a different GUI thread to the one on which the splash screen is running. We must, therefore, use invoke to get us onto the correct thread. This will be OK even if we are on the correct thread already. In fact we will use BeginInvoke so that the fade occurs asynchronously and does not block the Main GUI thread. If this method is called before the Main Application Thread is started then it will block the thread. However, there is nothing that can be done about that as in that case our thread will not have an active message pump and will therefore not respond to asynchronous method invocation. Application.DoEvents() is again used to process the Close request in case there is no message pump running.

//    Only fadeout if we are currently visible.
 if (Instance != null) {
  Instance.BeginInvoke(new MethodInvoker(Instance.Close));
    
  //    Process the Close Message on the Splash Thread.
  Application.DoEvents();
 }

The closing event itself must then assess if there is a main form present or not, so it can use a timer to fade out, or Thread.Sleep as in the fade in method. I am using the .NET 2.0 Framework property, Application.OpenForms here as the most reliable method I have found so far of testing if the Main Form of the Application is now displayed, or just the Splash screen. As soon as the Splash screen is 0% visible the form is closed properly and the Instance variable reset so that the splash screen cannot be closed twice, and so that it can be re-displayed as an about dialog. I have also included a Click event so that it can be used as an about dialog that closes on clicking.

if (Application.OpenForms.Count > 1) {
 if (this.Opacity > 0) {
  e.Cancel = true;
  this.Opacity -= 0.05;
        
  //    use the timer to iteratively call the close method thereby keeping
  //    the GUI thread available for other processes.
  this.fadeTimer.Tick -= new System.EventHandler(this.FadeoutTick);
  this.fadeTimer.Tick += new System.EventHandler(this.FadeoutTick);
  this.fadeTimer.Start();

Solving the z-order problem

The problem with the z-order was caused by running two message loops within the same AppDomain. As soon as the AppDomain receives the message loop exited message it deactivates the Application sending it to the back of the z-order. This is not normally a problem as this exited message would indicate the application is closing, but not in this case. Fortunately, this implementation of a splash screen avoids this problem by never starting a message loop for the splash screen, but rather pumping the messages manually.

Using the code

Calling the Splash screen is quite simple, once you have set the background image.

To Display the Splash Screen

Splash.ShowSplash();

To Display the Splash Screen with a fade in/out

 Splash.ShowSplash(500);

For this method you specify the number of milliseconds the splash should take to fade in or out.

To Hide the Splash Screen

Splash.Fadeout();

Please feel free to use this code in your own applications, adapting it as you see fit. I will appreciate any suggestions for improvements.

Happy coding

History

  • 26th October 2006 - First Release
  • 26th October 2006 - Updated to include reference to solving the z-order problem and clarifying the usage of message pumps/loops

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

The Man from U.N.C.L.E.
Software Developer
United Kingdom United Kingdom
Unfortunately my real name was already in use as a code project login. For those of you who are wondering I am really Napoleon Solo. Sorry, I mean, Mark Jackson. Well, I do look a bit like him I think.

Comments and Discussions

 
GeneralExcellent post PinmemberOwfAdmin26-Oct-07 21:05 
QuestionAdding progress bar control PinmemberMauricio Marzol2-Aug-07 8:54 
AnswerRe: Adding progress bar control PinmemberThe Man from U.N.C.L.E.2-Aug-07 23:00 
GeneralRe: Adding progress bar control PinmemberMauricio Marzol3-Aug-07 3:24 
GeneralRe: Adding progress bar control PinmemberThe Man from U.N.C.L.E.3-Aug-07 4:53 
Questionupdated? PinmemberRichElswick31-Jul-07 7:00 
AnswerRe: updated? PinmemberThe Man from U.N.C.L.E.31-Jul-07 7:59 
QuestionOther components PinmemberBlazetopher6-Jun-07 5:02 
NewsSuggestion - Timer / EventHandler Setup Pinmemberrtarquini8-Feb-07 3:15 
QuestionExcellent work, but do you need a timer? PinmemberVinceC2-Feb-07 13:35 
AnswerRe: Excellent work, but do you need a timer? PinmemberThe Man from U.N.C.L.E.4-Feb-07 23:29 
QuestionHow can I create a Splash Screen under Window presentation Framework? Pinmemberintelcoder21-Jan-07 6:03 
AnswerRe: How can I create a Splash Screen under Window presentation Framework? PinmemberThe Man from U.N.C.L.E.21-Jan-07 23:07 
GeneralVery nifty! Pinmembercormacmiller13-Dec-06 13:01 
GeneralRe: Very nifty! PinmemberHenk van der Geld26-Jun-07 11:14 
QuestionRe: Very nifty! Pinmembermeh_zor23-Oct-08 4:26 
GeneralUser authentication Pinmemberpalshete4-Dec-06 11:15 
GeneralRe: User authentication PinmemberThe Man from U.N.C.L.E.4-Dec-06 22:46 
GeneralRe: User authentication Pinmemberpalshete5-Dec-06 3:15 
GeneralRe: User authentication Pinmemberpalshete5-Dec-06 4:17 
GeneralGot it to work, but... PinmemberRichElswick1-Nov-06 18:31 
NewsRe: Got it to work, but... PinmemberThe Man from U.N.C.L.E.1-Nov-06 22:57 
GeneralReason for Splash - suggestion [modified] Pinmembergxdata30-Oct-06 20:17 
QuestionHow did you address the Z-Order problem? Pinmembersesmith2k226-Oct-06 3:16 
AnswerRe: How did you address the Z-Order problem? PinmemberThe Man from U.N.C.L.E.26-Oct-06 3:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140905.1 | Last Updated 14 Mar 2007
Article Copyright 2006 by The Man from U.N.C.L.E.
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid