using System;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Timers.Timer;
namespace Multi_threaded_Splash_Screen
{
public partial class SplashScreen : Form
{
/// <summary>
/// Contains information for creating a splash screen.
/// </summary>
private struct SplashScreenInfo
{
/// <summary>
/// The main form for the application.
/// </summary>
public Form StartupForm;
/// <summary>
/// The minimum number of milliseconds for which to display the splash screen.
/// </summary>
public int MinimumDisplayTime;
/// <summary>
/// Signals the main thread when a handler has been added to the main form's Load event.
/// </summary>
public ManualResetEvent Handle;
}
/// <summary>
/// Tells the splash screen when it has been open for the minimum amount of time.
/// </summary>
private Timer timer = new Timer();
/// <summary>
/// The minimum number of milliseconds for which to display the splash screen.
/// </summary>
private bool minimumDisplayTimeExpired = false;
/// <summary>
/// Used to synchronise multiple threads.
/// </summary>
private object syncRoot = new object();
/// <summary>
/// Signals the main thread when the splash screen is ready to close.
/// </summary>
private ManualResetEvent closeHandle;
/// <summary>
/// Creates a new instance of the SplashScreen class.
/// </summary>
/// <param name="startupForm">
/// The main form for the application.
/// </param>
/// <param name="minimumDisplayTime">
/// The minimum number of milliseconds for which to display the splash screen.
/// </param>
private SplashScreen(Form startupForm, int minimumDisplayTime)
{
InitializeComponent();
// We need to know when the main form is ready to be displayed.
startupForm.Load += startupForm_Load;
// We need to know when the splash screen is ready to be dismissed.
timer.Elapsed += timer_Elapsed;
timer.Interval = minimumDisplayTime;
timer.Start();
}
/// <summary>
/// Displays a splash screen.
/// </summary>
/// <param name="startupForm">
/// The main form for the application.
/// </param>
/// <param name="minimumDisplayTime">
/// The minimum number of milliseconds for which to display the splash screen.
/// </param>
public static void DisplaySplashScreen(Form startupForm, int minimumDisplayTime)
{
var continueHandle = new ManualResetEvent(false);
// Create and display the splash screen on a secondary thread.
new Thread(DisplaySplashScreen).Start(new SplashScreenInfo
{
StartupForm = startupForm,
MinimumDisplayTime = minimumDisplayTime,
Handle = continueHandle
});
// The handler must be added to the main form's Load event before the main thread can safely continue.
continueHandle.WaitOne();
}
/// <summary>
/// Displays a splash screen.
/// </summary>
/// <param name="info">
/// Contains information for creating a splash screen.
/// </param>
private static void DisplaySplashScreen(object info)
{
var ssi = (SplashScreenInfo) info;
var splashScreen = new SplashScreen(ssi.StartupForm, ssi.MinimumDisplayTime);
// The main form's Load event has been handled so the main thread can safely continue.
ssi.Handle.Set();
// Display the splash screen.
Application.Run(splashScreen);
}
private void startupForm_Load(object sender, EventArgs e)
{
lock (syncRoot)
{
if (!minimumDisplayTimeExpired)
{
// The splash screen is not ready to be dismissed so we must make the main form wait.
closeHandle = new ManualResetEvent(false);
}
}
if (closeHandle != null)
{
// The splash screen is not ready to be dismissed so we must make the main form wait.
closeHandle.WaitOne();
}
// Close the splash screen and allow the main form to be displayed.
this.Invoke(new MethodInvoker(this.Close));
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock (syncRoot)
{
if (closeHandle == null)
{
// The splash screen is ready to be dismissed before the main form is ready to be displayed.
// Allow the main form to be displayed as soon as it is ready.
minimumDisplayTimeExpired = true;
}
else
{
// The main form is already ready to be displayed and is waiting for the splash screen.
closeHandle.Set();
}
}
}
}
}