Introduction
Well, after scouring the Internet for a free splash screen component for .NET, I was left quite unhappy. The only decent ones around were by a company called BitLaboratory, and another CodeProject article published by Mr. Tom Clemont (link).
I had used BitLab's component previously, and was not overwhelmed with its design ... and they would not release the source. :/
So, here I was in the middle of several projects which all needed a splash page. And, I'm not one for your basic static splash screen. I like to show the user what's going on in the background. So, here is my DGDev - a splash screen assembly.
Background
The basic ideas I wanted to implement:
- Threading
- Custom background image
- Custom product/program information space
- Custom space for updated status
- Custom splash screen form shape (
TransparencyKey
)
Mr. Clemont's example served as a nice reference, but this has been entirely written from scratch.
Example
- ScreenCam for the project implementation: Click here.
Full code
The actual code is very short, and very clear to understand.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace DGDev
{
public class SplashScreen : Form
{
private const double OpacityDecrement = .08;
private const double OpacityIncrement = .05;
private const int TimerInterval = 50;
private static Boolean FadeMode;
private static Boolean FadeInOut;
private static Image BGImage;
private static String Information;
private static String Status;
private static SplashScreen SplashScreenForm;
private static Thread SplashScreenThread;
private static Color TransparentKey;
private Label ProgramInfoLabel;
private Label StatusLabel;
private System.Windows.Forms.Timer SplashTimer;
private IContainer components;
private delegate void UpdateLabel();
private delegate void CloseSplash();
#region Public Properties & Methods
public String SetInfo
{
get { return Information; }
set
{
Information = value;
if(ProgramInfoLabel.InvokeRequired)
{
var InfoUpdate =
new UpdateLabel(UpdateInfo);
Invoke(InfoUpdate);
}
else
{
UpdateInfo();
}
}
}
public String SetStatus
{
get { return Status; }
set
{
Status = value;
if(StatusLabel.InvokeRequired)
{
var StatusUpdate =
new UpdateLabel(UpdateStatus);
Invoke(StatusUpdate);
}
else
{
UpdateStatus();
}
}
}
public Image SetBackgroundImage
{
get { return BGImage; }
set
{
BGImage = value;
if (value != null)
{
BackgroundImage = BGImage;
ClientSize = BackgroundImage.Size;
}
}
}
public Color SetTransparentKey
{
get { return TransparentKey; }
set
{
TransparentKey = value;
if (value != Color.Empty)
TransparencyKey = SetTransparentKey;
}
}
public Boolean SetFade
{
get { return FadeInOut; }
set
{
FadeInOut = value;
Opacity = value ? .00 : 1.00;
}
}
public static SplashScreen Current
{
get
{
if (SplashScreenForm == null)
SplashScreenForm = new SplashScreen();
return SplashScreenForm;
}
}
public void SetStatusLabel(Point StatusLabelLocation,
Int32 StatusLabelWidth, Int32 StatusLabelHeight)
{
if (StatusLabelLocation != Point.Empty)
StatusLabel.Location = StatusLabelLocation;
if (StatusLabelWidth == 0 && StatusLabelHeight == 0)
StatusLabel.AutoSize = true;
else
{
if (StatusLabelWidth > 0)
StatusLabel.Width = StatusLabelWidth;
if (StatusLabelHeight > 0)
StatusLabel.Height = StatusLabelHeight;
}
}
public void SetInfoLabel(Point InfoLabelLocation,
Int32 InfoLabelWidth, Int32 InfoLabelHeight)
{
if (InfoLabelLocation != Point.Empty)
ProgramInfoLabel.Location = InfoLabelLocation;
if (InfoLabelWidth == 0 && InfoLabelHeight == 0)
ProgramInfoLabel.AutoSize = true;
else
{
if (InfoLabelWidth > 0)
ProgramInfoLabel.Width = InfoLabelWidth;
if (InfoLabelHeight > 0)
ProgramInfoLabel.Height = InfoLabelHeight;
}
}
public void ShowSplashScreen()
{
SplashScreenThread = new Thread(ShowForm)
{IsBackground = true, Name = "SplashScreenThread"};
SplashScreenThread.Start();
}
public void CloseSplashScreen()
{
if (SplashScreenForm != null)
{
if(InvokeRequired)
{
var ClosingDelegate =
new CloseSplash(HideSplash);
Invoke(ClosingDelegate);
}
else
{
HideSplash();
}
}
}
#endregion
public SplashScreen()
{
InitializeComponent();
}
private static void ShowForm()
{
Application.Run(SplashScreenForm);
}
private void UpdateStatus()
{
StatusLabel.Text = SetStatus;
}
private void UpdateInfo()
{
ProgramInfoLabel.Text = SetInfo;
}
private void SplashTimer_Tick(object sender, EventArgs e)
{
if(FadeMode)
{
if (Opacity < 1.00)
Opacity += OpacityIncrement;
else
SplashTimer.Stop();
}
else
{
if(Opacity > .00)
Opacity -= OpacityDecrement;
else
Dispose();
}
}
#region InitComponents
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.ProgramInfoLabel = new System.Windows.Forms.Label();
this.StatusLabel = new System.Windows.Forms.Label();
this.SplashTimer = new System.Windows.Forms.Timer
(this.components);
this.SuspendLayout();
this.ProgramInfoLabel.BackColor =
System.Drawing.Color.Transparent;
this.ProgramInfoLabel.Location =
new System.Drawing.Point(56, 52);
this.ProgramInfoLabel.Name = "ProgramInfoLabel";
this.ProgramInfoLabel.Size =
new System.Drawing.Size(100, 23);
this.ProgramInfoLabel.TabIndex = 0;
this.StatusLabel.BackColor =
System.Drawing.Color.Transparent;
this.StatusLabel.Location =
new System.Drawing.Point(59, 135);
this.StatusLabel.Name = "StatusLabel";
this.StatusLabel.Size = new System.Drawing.Size(100, 23);
this.StatusLabel.TabIndex = 1;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.StatusLabel);
this.Controls.Add(this.ProgramInfoLabel);
this.FormBorderStyle =
System.Windows.Forms.FormBorderStyle.None;
this.Name = "SplashScreen";
this.ShowInTaskbar = false;
this.StartPosition =
System.Windows.Forms.FormStartPosition.CenterScreen;
this.ResumeLayout(false);
}
#endregion
private void SplashScreen_Load(object sender, EventArgs e)
{
if (SetFade)
{
FadeMode = true;
SplashTimer.Interval = TimerInterval;
SplashTimer.Start();
}
}
private void HideSplash()
{
if(SetFade)
{
FadeMode = false;
SplashTimer.Start();
}
else
Dispose();
}
}
}
Using the DLL
Let's take a quick glimpse at another program I'm working on, which is now fit for using the splash screen.
Program.cs
using System;
using System.Drawing;
using System.Windows.Forms;
using DGDev;
namespace Raum
{
static class Program
{
private static SplashScreen splash;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
const String resourceName = "Splash.png";
var InfoLabel = new Point(25, 92);
var StatusLabel = new Point(28, 261);
splash = SplashScreen.Current;
splash.SetTransparentKey = Color.Fuchsia;
var rs = GetResourceStream(resourceName);
splash.SetBackgroundImage = new Bitmap(rs);
splash.SetInfoLabel(InfoLabel, 499, 117);
splash.SetStatusLabel(StatusLabel, 490, 17);
splash.ShowSplashScreen();
splash.SetInfo = "This program is just an example.";
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyLoad += asmLoadHandler;
Application.Run(new RaumMain());
}
private static System.IO.Stream GetResourceStream(String resource)
{
var ea = System.Reflection.Assembly.GetExecutingAssembly();
foreach (String curResource in ea.GetManifestResourceNames())
{
if (curResource.EndsWith(resource))
{
return ea.GetManifestResourceStream
(curResource);
}
}
return null;
}
static void asmLoadHandler(object sender, AssemblyLoadEventArgs args)
{
splash.SetStatus = "Loading Assembly: " +
args.LoadedAssembly.GetName().Name + " ...";
}
}
}
A closer look
So, what's going on up there?!? By using this SplashScreen.dll, you can literally have your splash screen done in a minute, with several options to play with.
const String resourceName = "Splash.png";
var InfoLabel = new Point(25, 92);
var StatusLabel = new Point(28, 261);
splash = SplashScreen.Current;
splash.SetTransparentKey = Color.Fuchsia;
var rs = GetResourceStream(resourceName);
splash.SetBackgroundImage = new Bitmap(rs);
(Point Location, Int32 Width, Int32 Height)
splash.SetInfoLabel(InfoLabel, 499, 117);
splash.SetStatusLabel(StatusLabel, 490, 17);
splash.ShowSplashScreen();
splash.SetInfo = "This program is just an example.";
Dynamically loading updates
By preference, I like to show the user what .NET assemblies are being loaded. Here, we see that whenever an assembly is loaded, it updates the status label on the splash screen.
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyLoad += asmLoadHandler;
static void asmLoadHandler(object sender, AssemblyLoadEventArgs args)
{
splash.SetStatus = "Loading Assembly: " +
args.LoadedAssembly.GetName().Name + " ...";
}
Closing the splash screen
A great feature about this class is that it can be accessed from anywhere in your code. So, after your Main
form has loaded, just use the following to close the splash screen:
splash = SplashScreen.Current;
splash.CloseSplashScreen();
Points of interest
Known issues:
- When using a custom shape form/
TransparencyKey
, you cannot use the SetFade
property. If you do, you'll notice that the TransparencyKey
does not work until the Form.Opacity
is at 1.00.
Questions/Problems/Suggestions
I have not yet implemented try
/catch
blocks, so exceptions are not being handled at the moment. Will do more testing, and add new features along with proper exception handling.
If you have a problem, feel free to email me, or post in the discussion forum below.
If you're using this for proprietary commercial software, help a broke college student out and donate a dollar.
History
- 10.13.2008 - v1.0.0.0 released.
- 10.24.2008 2:05pm - Fixed the demo download link. Sorry guys.
- 10.23.2008 4:42pm - Uploaded new files. There were a few bugs to be fixed. Code should now work with all .NET Framework versions. Also, the
SetFade
is fixed.
- 10.13.2008 10:08pm - Sorry guys, after refactoring the code automatically with JetBrains, it took away some needed assembly constructors in the designer code. Reshaper tends to think everything is redundant, sometimes. I've updated the fixed file.