As developers we are always trying to put some sort of cool functionality in our applications. We cant help it, its our nature. If it wasnt wed 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.
Im not going give a tutorial on the hows of creating splash screen form. There are a million and one articles on how to do this. What Im 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, inheriting from it and putting our splash screen logic into it. This way you dont 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.
ApplicationContext: What does it do?
Im 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 dont 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. Lets take a look at a standard Main() function of a WinForms application.
static void Main()
Im sure youve 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 in 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 server 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 applications user interface is running in, and it is the work horse processes the applications 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 lets 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 applications main form. RunMessageLoop then registers ThreadContexts callback function OnAppThreadExit with the ApplicationContexts ExitThread event. This will let the the message loop get notified when the user closes the applications main form(which Ill talk about shortly). RunMessageLoop then sets the main forms 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 called MainForm. In the Main function code example we went over, the Form instance that we passed into the ApplicationContexts constructor gets set to the MainForm property. The set_MainForm property will register the ApplicationContext.OnMainFormDestroy callback function with the passed in forms 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 too. 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 ApplicationContexts OnMainFormDestroy callback function. This callback then invokes the ThreadContexts ExitThread event, which calls the ThreadContexts 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 were going to do in this next section.
Microsoft didnt 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 thats just what were going to do, make a customized ApplicationContext and holds two forms, a splash screen form and the applications main form.
First create a WinForms project and add a second form to 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:
public class 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:
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 timers default values. The timer object will wait, by default for two seconds and then call the SplashTimeUp callback function.