Building a splash screen. (err... not taking a bath)

So one requirement we had this week was to add a splash screen to the project we're working on. My knowledge of threading is weak, so bare with me. As our application started to grow, the start up times started to grow so a splash screen is supposed to be a queue to the user that yes the app is running.

This is what the end result ended up...

1 using (new BackgroundThread(new DisplaySplashScreenCommand()))
2   {
3       ApplicationStartUpTask.ApplicationBegin();
4   }

So what's happening is the Splash screen is loaded on a background thread, while the application start up continues on the main thread. When the application start up is finished, the background thread disposes of the command that it's executing. In this case it starts fading the splash screen away.

Here's the core interface that made this happen.

1 public interface ICommand
2   {
3       void Execute();
4   }
1 public interface IDisposableCommand : ICommand, IDisposable
2   {
3   }
4 
5   public interface IBackgroundThread : IDisposable
6   {
7   }

This is a pretty simple solution (IMHO). The actual splash screen is just a win form, that starts a timer and adjusts the opacity when it's asked to display, then fade away when it's asked to hide.

 1 public partial class SplashScreen : Form, ISplashScreen
 2   {
 3       private Timer timer;
 4 
 5       public SplashScreen()
 6       {
 7           InitializeComponent();
 8           Visible = false;
 9       }
10 
11       public void DisplayTheSplashScreen()
12       {
13           ApplyWindowStyles();
14           StartFadingIn();
15       }
16 
17       public void HideTheSplashScreen()
18       {
19           StartFadingOut();
20       }
21 
22       private void StartFadingIn()
23       {
24           Opacity = .0;
25           timer = new Timer {Interval = 50};
26           timer.Tick += ((sender, e) => { if (Opacity < 1) Opacity += .05; });
27           timer.Start();
28           ShowDialog();
29       }
30 
31       private void StartFadingOut()
32       {
33           if(timer != null &amp;&amp; timer.Enabled){
34               timer.Stop();
35           }
36           timer = new Timer {Interval = 50};
37           timer.Tick += (delegate {
38                                  if (Opacity > 0) {
39                                      Opacity -= .1;
40                                  }
41                                  else {
42                                      timer.Stop();
43                                      Close();
44                                  }
45                              });
46           timer.Start();
47       }
48 
49       private void ApplyWindowStyles()
50       {
51           BackgroundImage = Image.FromFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images/splash.jpg"));
52           FormBorderStyle = FormBorderStyle.None;
53           StartPosition = FormStartPosition.CenterScreen;
54           ClientSize = BackgroundImage.Size;
55           TopMost = true;
56       }
57   }

For starting and running a non-blocking background thread, we're using the BackgroundWorker class which takes care of thread synchronization. (This comes in handy for synchronizing UI elements)

 1 public class BackgroundThread : IBackgroundThread
 2   {
 3       private readonly BackgroundWorker worker_thread;
 4 
 5       public BackgroundThread(IDisposableCommand command)
 6       {
 7           worker_thread = new BackgroundWorker();
 8           worker_thread.DoWork += delegate { command.Execute(); };
 9           worker_thread.Disposed += delegate { command.Dispose(); };
10           worker_thread.RunWorkerAsync();
11       }
12 
13       public void Dispose()
14       {
15           worker_thread.Dispose();
16       }
17   }

Just so you get the rest of the code, here's the DisplaySplashScreenCommand, although I'm sure it was obvious.

 1 public class DisplaySplashScreenCommand : IDisposableCommand
 2   {
 3       private ISplashScreen splash_screen;
 4 
 5       public DisplaySplashScreenCommand() : this(new SplashScreen())
 6       {
 7       }
 8 
 9       public DisplaySplashScreenCommand(ISplashScreen splash_screen)
10       {
11           this.splash_screen = splash_screen;
12       }
13 
14       public void Execute()
15       {
16           splash_screen.DisplayTheSplashScreen();
17       }
18 
19       public void Dispose()
20       {
21           splash_screen.HideTheSplashScreen();
22       }
23   }

Hope this helps anyone out there trying to implement a splash screen. PS. I can't stress the fact that my knowledge of Threading is limited, so if you know of a cleaner implementation... Please, please hook a brotha up!

The end result looks like...

splash_screen

comments powered by Disqus