As developers, we are always trying to put some sort of ‘cool’ functionality in our applications. We can’t help it, it’s our nature. If it wasn’t, we’d be working in Accounting or Marketing. One way to add a little bit of ‘coolness’ to our WinForm applications is to add a splash screen that pops up for a few seconds when the application starts.
I’m not going to give a tutorial on the hows of creating splash screen forms. There are a million and one articles on how to do this. What I’m going to go over is a clean way to encapsulate splash screen functionality into a helper class. We are going to use a class called the
ApplicationContext class, inherit from it and put our splash screen logic into it. This way you don’t have to put any special code into your main or splash forms to make them work. In fact, once you implement your custom
ApplicationContext class, you can use any form you want, and turn it into a splash screen.
Application Context: What does it do?
I’m going to spend some time going over, in detail, what the
ApplicationContext does. This will make it easier to understand what our inherited class is doing behind the scenes. If you don’t really care about what the
ApplicationContext does, just skip ahead to the section called ‘Putting it all together’.
So what does the
ApplicationContext do? Not too much really, which is what makes it so easy to configure it to our needs. Every WinForms application has an instance of an
ApplicationContext class, you just may not know about it. Let's take a look at a standard
Main() function of a WinForms application.
static void Main()
I’m sure you’ve seen this before. But did you know you could also write it this way?
static void Main()
ApplicationContext appCtx = new ApplicationContext(new Form1());
They are really one and the same. In the first case, the
Application.Run function just creates a new
ApplicationContext instance and passes the form object into its constructor. In the second case, we did all this manually. So for now, just remember that every WinForm application has one
ApplicationContext instance, and it holds an instance to the
Form object that will serve as our main form.
The purpose of the
ApplicationContext is to serve as an application start and termination notification link between the main
Form in your application and the UI thread. The UI thread is the main thread that your application's user interface is running in, and it is the work horse that processes the application’s message loop. The message loop receives event messages from the operating system like ‘Mouse Right Click’ or ‘Space Bar Button Down’, and sends these messages to the form that needs to handle it.
The message loop is inside the
ThreadContext class, which is a private sub class, defined inside the
Application class. There is very little documentation on the internals of the
ThreadContext, but you can peek inside it using the Anikrino tool.
So let's go back to the
Main function. When
Application.Run is called from
Main(), it in turn calls
ThreadContext.RunMessageLoop, passing in the newly created
ApplicationContext instance, which contains an instance to the application’s main form.
RunMessageLoop then registers
ThreadContext’s callback function
OnAppThreadExit with the
ExitThread event. This will let the message loop get notified when the user closes the application’s main form (which I’ll talk about shortly).
RunMessageLoop then sets the main form’s
Visible property to
true, which is the point that the user finally sees the main form. The final thing
RunMessageLoop does is enter into the actual message loop so it can start receiving and processing event messages from the operating system.
ApplicationContext class has only one property, which is called
MainForm. In the
Main function code example we went over, the Form instance that we passed into the
ApplicationContext’s constructor gets set to the
MainForm property. The
set_MainForm property will register the
ApplicationContext.OnMainFormDestroy callback function with the passed in form’s
HandleDestroyed event, which will get invoked when the user closes the form. This way the
ApplicationContext will get notified whenever the main form of the application ever gets destroyed.
This callback is very important because in WinForms applications, forms are not the application, they are just objects that the
Application object holds a reference to. If the
Application object did not get notified when the main form was destroyed, it would continue to run the message loop endlessly. So when the user closes the main form, the form invokes its
HandleDestroyed event, which calls the
OnMainFormDestroy callback function. This callback then invokes the
ExitThread event, which calls the
OnAppThreadExit callback function. Calling this function tells the
ThreadContext that the main form of the application has been destroyed and it is ok to terminate the UI thread. It posts the application quit message to the operating system, which will cause the application to clean up any resources it needs to and terminate the UI thread.
Putting it all together to create a splash screen
That was probably more than you ever wanted to know about what happens when the
Main function calls
Application.Run. But I wanted to go over it so you would understand exactly why we are doing what we’re going to do in this next section.
Microsoft didn’t have to expose the
ApplicationContext class for us to use, but they did, so we could inherit from it and customize this startup process. And that’s just what we’re going to do, make a customized
ApplicationContext and hold two forms, a splash screen form and the application’s main form.
First create a WinForms project and add a second form to it called Splash.cs. Next add a new class to your project and call it
SplashAppContext. The first thing you want to do is make this class inherit from
public class SplashAppContext : ApplicationContext
. . .
We also need a private field of type
Form and another of type
Form mainForm = null;
Timer splashTimer = new Timer();
Next create a constructor for
SplashAppContext. This constructor will have two input properties, both of type
Form. The first one is your splash screen form and the second one is your main application form:
public SplashAppContext(Form mainForm, Form splashForm) : base(splashForm)
this.mainForm = mainForm;
splashTimer.Tick += new EventHandler(SplashTimeUp);
splashTimer.Interval = 2000;
splashTimer.Enabled = true;
Be sure to call the base constructor and pass in the splash screen form instance. This will cause the base
ApplicationContext to store the splash screen form in its
MainForm property. The constructor then stores off the main application form in its private field and sets up the timer’s default values. The timer object will wait, by default for two seconds, and then calls the
SplashTimeUp callback function.
private void SplashTimeUp(object sender, EventArgs e)
splashTimer.Enabled = false;
SplashTimeUp gets called, after a two second delay, it first disposes off the timer object, and then closes the base
ApplicationContext’s main form. This will cause the splash screen to close itself. Remember that when the
MainForm property gets set, it registers the
OnMainFormClosed callback function with the form’s
HandleDestroyed event? Well, when we call
base.MainForm.Close(), this triggers the splash screen’s
HandleDestroyed event, which calls the
protected override void OnMainFormClosed(object sender, EventArgs e)
if (sender is Splash)
base.MainForm = this.mainForm;
else if (sender is Form1)
In the callback function, we first check to see if the closed form, the sender object, was the splash screen. If it is, we replace the splash screen form stored in the
base.MainForm property with the application’s main form instance. Remember what I just said setting this property did? It registers the
OnMainFormClosed with the form’s
HandleDestroyed event. So now that we’ve reset the
MainForm property, when the user closes the main application form,
OnMainFormClosed will get called a second time.
This time, we check the closed form to see if it is the main application form. If it is, we call
base.OnMainFormClosed, which will invoke the
ThreadExit event, which in turn calls the
OnAppThreadExit call back function, cleaning up any resources and terminating the UI thread.
I also added a public property to this class to expose the splash screen timer, in case you want to change the default splash screen delay.
public int SecondsSplashShown
splashTimer.Interval = value * 1000;
Now that we have this class, using it is very easy. First create an instance of
SplashAppContext, and pass in the main application form in the first parameter, and the splash screen form as the second parameter. Then just call
Application.Run, passing in the new instance of
static void Main()
SplashAppContext splashContext =
new SplashAppContext(new Form1(), new Splash());
ApplicationContext gets passed to
ThreadContext.RunMessageLoop, the function will set the
visible property of the splash screen to
true, showing the splash screen to the user. After two seconds, the timer calls its callback function which closes the splash screen. This triggers the
OnMainFormClosed function to be called, which swaps the splash screen form with the main application form in the
ApplicationContext.MainForm property, and then shows the main form to the user. This is all the code you need to put in your application’s
Main function. The splash screen function doesn’t need any code at all, its all taken care of in the custom
I prefer this model because it encapsulates all the splash screen logic away from the forms. The forms just have to sit there and look pretty, nothing else. With this model, you can pass in any two forms into the
SplashAppContext constructor and they will work.
You can also take this class and expand on it to make a ‘cooler’ splash screen, one with more bells and whistles. I created a second class that inherits from
SplashFadeAppContext, that lets you configure the splash screen to fade in, fade out, both, or neither. This class is in the same cs file as the
SplashAppContext class, just below it.
I have been a professional developer since 1996. My experience comes from many different industries; Data Mining Software, Consulting, E-Commerce, Wholesale Operations, Clinical Software, Insurance, Energy.
I started programming in the military, trying to find better ways to analyze database data, eventually automating my entire job. Later, in college, I automated my way out of another job. This gave me the great idea to switch majors to the only thing that seemed natural…Programming!