Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Splash Form and Loading Data in a Separate Thread

0.00/5 (No votes)
7 Apr 2006 1  
How to display a splash form and load data in a separate thread
image 1

image 2

Introduction

I believe every application developer has experienced the problem with slow loading user interface because loading data takes too much time.

While for Web applications, it might seem more natural because of generating the page on the fly, using slow internet connection or overloaded server, it is not the case for Windows Forms applications. They run locally on your machine and it is expected to be more responsive. Still too often, you can see applications that take too much time to update the user interface and display the data.

This article will try to show you how to make a Windows Forms application more responsive by showing the user interface while waiting for the data and updating the interface when data is available.

The article demonstrates few things:

  • How to build a splash form to be displayed during a time consuming operation
  • Performing the time consuming operation in a separate thread
  • Center the splash form relative to main form
  • Move the splash screen when the main form is moved to keep it centered to it
  • Hide / show the splash screen when the main form gets / loses focus

The solution consists of two forms:

  • MainForm - This is the main form as the name suggests
  • SplashFrm - The splash form

MainForm

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace SplashScreen
{
  /// <summary>
  /// Summary description for MainForm.
  /// </summary>
  public class MainForm : System.Windows.Forms.Form
  {
    private System.ComponentModel.Container components = null;
    private System.Windows.Forms.Label label1;
    private System.Windows.Forms.StatusBar statusBar1;

    // instance member to keep reference to splash form
    private SplashForm frmSplash;

    // delegate for the UI updater
    public delegate void UpdateUIDelegate(bool IsDataLoaded);

    //
    // Some of the code was removed for simplicity
    //

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      Application.Run(new MainForm());
    }

    /// <summary>
    /// Handles the Load event of the MainForm control.
    /// </summary>
    private void MainForm_Load(object sender, System.EventArgs e)
    {
      // Update UI
      UpdateUI(false);

      // Show the splash form
      frmSplash = new SplashForm(this);

      // Do some time consuming work in separate thread
      Thread t = new Thread(new ThreadStart(DoSomeWork));
      t.IsBackground = true;
      t.Start();
    }

    /// <summary>
    /// Time consuming method
    /// </summary>
    private void DoSomeWork()
    {
      // This is time consuming operation - loading data, etc.
      System.Threading.Thread.Sleep(10000);

      // Update UI
      Invoke(new UpdateUIDelegate(UpdateUI), new object[] { true });
    }

    /// <summary>
    /// Updates the UI.
    /// </summary>
    private void UpdateUI(bool IsDataLoaded)
    {
      if (IsDataLoaded)
      {
        this.statusBar1.Text = "Done.";

        // close the splash form
        if (this.frmSplash != null) {
          frmSplash.Close();
        }
      }
      else
      {
        this.statusBar1.Text = "Loading data ...";
      }
    }
  }
}

The code is very straightforward. At the time the main form is loaded, the UpdateUI() method is invoked to update the status bar. Then the splash form is shown and immediately after that, DoSomeWork() method is executed in a separate thread.

By using this technique, the main form is displayed without any delay and the splash form is telling the user that the data is loading. If we do not use a separate thread to load the data, it might take a few seconds for the main form to appear. It is all about the user experience.

When the data is loaded, we have to update the user interface - change the status bar text and close the splash form. Since loading of the data happened in a separate thread, we are not supposed to update the main form directly. Instead, we are going to use the main form's Invoke method passing the delegate to UpdateUI() method with a parameter telling the data loading is complete.

Now let's have a look at the splash form.

SplashForm

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace SplashScreen
{
  /// <summary>
  /// Summary description for SplashForm.
  /// </summary>
  public class SplashForm : System.Windows.Forms.Form
  {
    private System.Windows.Forms.Panel panel1;
    private System.Windows.Forms.Label label1;
    private System.ComponentModel.Container components = null;

    // instance member to keep a reference to main form
    private Form MainForm;

    // flag to indicate if the form has been closed
    private bool IsClosed = false;

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="SplashForm" /> class.
    /// </summary>
    public SplashForm()
    {
      InitializeComponent();
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="SplashForm" /> class.
    /// </summary>
    public SplashForm(Form mainForm):this() {
      // Store the reference to parent form
      MainForm = mainForm;

      // Attach to parent form events
      MainForm.Deactivate += new System.EventHandler(this.MainForm_Deactivate);
      MainForm.Activated += new System.EventHandler(this.MainForm_Activated);
      MainForm.Move += new System.EventHandler(this.MainForm_Move);

      // Adjust appearance
      this.ShowInTaskbar = false; // do not show form in task bar
      this.TopMost = true; // show splash form on top of main form
      this.StartPosition = FormStartPosition.Manual;
      this.Visible = false;

      // Adjust location
      AdjustLocation();
    }

    #endregion

    #region Methods

    private void MainForm_Deactivate(object sender, EventArgs e)
    {
      if (!this.IsClosed)
      {
        this.Visible = false;
      }
    }

    private void MainForm_Activated(object sender, EventArgs e)
    {
      if (!this.IsClosed)
      {
        this.Visible = true;
      }
    }

    private void MainForm_Move(object sender, EventArgs e)
      {
        // Adjust location
        AdjustLocation();
      }

    private void SplashForm_Closed(object sender, System.EventArgs e)
    {
      this.IsClosed = true;
    }

    private void AdjustLocation()
      {
        // Adjust the position relative to main form
        int dx = (MainForm.Width - this.Width) / 2;
        int dy = (MainForm.Height - this.Height) / 2;
        Point loc = new Point(MainForm.Location.X, MainForm.Location.Y);
        loc.Offset(dx, dy);
        this.Location = loc;
      }

    #endregion
  }
}

In addition to the default constructor, we have another one that accepts as a parameter a reference to the main form. We are using this one inside MainForm_Load():

  // Show the splash form
  frmSplash = new SplashForm(this);
  frmSplash.Show();  

We need the main form reference in order to be able to adjust the position of splash form and its visibility when those change for the main form.

That's all about it.

Conclusion

I would be glad if this article helps someone trying to provide a better user experience for WinForm applications. I am open to any suggestions or questions.

Known Issues

The solution is still not perfect. If you start another application, the main form will lose focus and the splash form will be hidden. However, if you minimize that other application and main form gets visible, the splash form will not show unless you click the main form giving the focus to it in this way.

History

  • 29th March, 2006 - Initial version
  • 7th April, 2006 - Show/hide splash form when main form gets / loses focus; splash form follows main form when the latter is moved across the screen

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.

A list of licenses authors might use can be found here