Use the ApplicationContext Class to Fully Encapsulate Splash Screen Functionality

This isn't an example on how to create a splash screen for your app. This article explains a clean way to encapsulate splash screen functionality into an inherited ApplicationContext class. This article also shows in detail what happens behind the scenes when a WinForm app starts.

Introduction

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.

staticvoid Main()
{
Application.Run(new Form1());
}

I’m sure you’ve seen this before. But did you know you could also write it this way?

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 ApplicationContext’s 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.

The 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 ApplicationContext’s OnMainFormDestroy callback function. This callback then invokes the ThreadContext’s ExitThread event, which calls the ThreadContext’s 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 ApplicationContext:

publicclass SplashAppContext : ApplicationContext
{
. . .

We also need a private field of type Form and another of type Timer:

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:

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.

When 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 ApplicationContext’s MainForm property gets set, it registers the ApplicationContext’s 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 OnMainFormClosed function:

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 ThreadContext’s 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.

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 SplashAppContext.

When the 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 ApplicationContext.

Conclusion

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 ApplicationContext, called 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

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!

The ApplicationContext holds the reference to the Splash form over the Application.ThreadContext.RunMessageLoopInner method, where the instance is passed as an argument.

Hope you have an idea how to solve this.

Now my suggestion:
I also found out that in your program the Tick event of the timers got not disconnect befor you dispose them.
So the timermemory in SplashFadeAppContext class will not get freed by the GC.
I changed you code liek this, and now it's ok: