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!

People are using this article as a solution for showing a login form; see Help - Main Form Closing whole Application - C#[^]. The suggestion has been to use the techniques described in this article. This article might be a very good way to make things better, but I want people to understand it is not necessary for a basic solution.

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:

Hi , I know this mesage probably doesnt belong here .But I need to know how to architect a windows application solution .

Specifically I will be having a MDI applications which will be the MOTHER of all applications to follow in my departmnet .
It is going to be on the lines of Visual Studio .NET IDE ( obviosuly not that awesome )

Its a daunting task .
So i need design guidelines as to how I package my forms .

How do i do inheritance with WinForms ?
How and where do i place the general mouse , keyboard event handling ??
SHould i use a reusable control approach ??

Hi , I know this mesage probably doesnt belong here .But I need to know how to architect a windows application solution .

Specifically I will be having a MDI applications which will be the MOTHER of all applications to follow in my departmnet .
It is going to be on the lines of Visual Studio .NET IDE ( obviosuly not that awesome )

Its a daunting task .
So i need design guidelines as to how I package my forms .

How do i do inheritance with WinForms ?
How and where do i place the general mouse , keyboard event handling ??

Sure that would work. I've seen it done a few times in production code. When i wrote this I was trying to figure out what is going on behind the sceens in WinForms, and used the splash screen as a way to play around and share what I learnd. There are 101 ways to do just about anything in .Net. There is rarely a 'best' way for all situations, just a more appropriate way for the situation at hand. Learn all the tools and pick the appropriate one for the current situation. This article just shows one way of implementing a spash screen. The reason i liked it vs having two Application.Run calls is that you would have to put logic into the formSplash to know when to close it.

At the moment the classes need to reside in the same project namespace as the calling form. I moved them out into my standard forms namespace e.g. CreativeNRG.Forms created a new forms project and added a reference to CreativeNRG.Forms. Now the method OnMainFormClosed knows nothing about Form1(or whatever the main form might be called) and won't build.

Maybe this is just my code, because I did make some changes although they were very small and almost nothing at all, but I've found that doing the splash screen this way causes an interesting problem!

If you load the splash screen and the main form in the SplahAppContext, if you have a SaveFileDialog and try to overwrite a file the dialog automatically shows a MessageBox asking if you want to continue. I'm assuming this MessageBox's owner is the main window passed to Application.Run because I get an exception when the MessageBox is supposed to appear. The Exception states that I cannot access "Splash" because it is a disposed object. Any ideas on how to correct this?

This is a great article. I was able to apply it to my project. But, I am having the same issue. It seems that the Splash form is disposed but still gets messages when using the SaveFileDialog control with Overwrite prompt enabled.
Does this happen because the Splash reference has not been garbage collected? Any help would be appreciated.

I assigned the splashForm reference to a splashForm private member variable in my concrete ApplicationContext instead of setting it into the base.MainForm property in the base ApplicationContext.

<br/>
public class MyApplicationContext : ApplicationContext<br/>
{<br/><br/><br/>
//This is private since setting it to the ApplicationContext.MainForm causes an exception if using the FileSaveDialog user<br/>
//control<br/>
private Form splashForm = null;<br/><br/>
...<br/><br/>
}<br/><br/>

I call show() and close() directly on the private splashForm reference instead of on the base.MainForm property.
In summary, the base ApplicationContext no longer manages or holds on to my splashForm reference.

So it appears the ThreadContext holds onto a currentForm which in the beginning is set to the context.MainForm (ApplicationContext.MainForm which is Form1 in our case). I would theorize that when you update the ApplicationContext.MainForm ( say to Form2 ), then the ThreadContext.currentForm doesn't get updated. Then when the second form ( Form2 ) tries to open a modal dialog ( the SaveFileDialog overwrite prompt modal dialog ) then ultimately the following function in ThreadContext gets called which is trying to access this.currentForm ( ThreadContext.currentForm ) which is still pointing at Form1.

I was using this technique in my program to implement a login screen before showing the main form. Then I ran into this problem when I did a MessageBox.Show(). I had to scrap the whole thing and do it differently. Oh well, at least I learned a little about what goes on behind the scenes in a Winforms App.

I've reproduced and looked at this problem in detail and there is no public way to set the "currentForm" field in the ThreadContext class. There are two other options available. You can use Reflection and FieldInfo.SetValue() to replace the spash form instance with the main form instance in the currentForm field. Or you could pin both forms in memory and do a memory swap, that way the ThreadContext.currentForm would point to a copy of memory that is exactly like the Main form. The Reflection way is easier to code, but takes long to execute, though is testing it I didnt see a performance issue with using it.

So to use the Reflection method replace the OnMainFormClosed method with this code:

Now note that using Reflection to interact with internal and private members of a class should be used with a warning. Microsoft could change the internal workings of a class with any service pack or new version, so if you use this, be sure to test it before deploying a new version of the .Net Framework.

Also, i did test this against a few applications and ran into no problems. But changing the private field of the ThreadContext could cause some serious issues that i havent tested for (very much like the problem you guys just pointed out!) so besure to make sure to do a full regression test on your app before deploying anything with this code.
Thanks,
John Conwell

I am trying to convert this example to VB .NET, but I am not sure how to implement an interface in VB. Is Not 'public SplashAppContext(Form mainForm, Form splashForm) : base(splashForm)' where : base(splashForm) is an interface? Not a strong C# developer so I maybe WAY off on this.