IntroductionEvery time a customer loads your application, you have the opportunity to impress or disappoint with your splash screen. A good splash screen will:
In this tutorial, we'll explore how to create a splash screen and add these features one at a time. We start with the creation of a simple splash screen, followed by the code changes required for the addition of each feature. You can skip to the bottom of the article to see the complete source code. I've also included a small test project in the download that demonstrates the splash screen. BackgroundIt was a lot of fun writing the code for this article and, while it's not perfect for all needs, I hope it saves you some coding time. The most fun for me is seeing a completely accurate and smoothly progressing progress bar as my application loads up. Please feel free to post any enhancement suggestions, bugs or other comments you may have. Create the Simple Splash Screen ProjectStart out by creating a Windows Forms project. Name it SplashScreen. Add a Windows Form to the project and name it SplashScreen. Delete Form1.cs.Now obtain a product bitmap with a light background suitable for putting text over. If you're lucky, a really talented person (like dzCepheus - see the thread below) will provide one for you. Set the Background Image property to it. Set the following properties on the form: FormBorderStyle = None
StartPosition = CenterScreen
In the form constructor, add the line: this.ClientSize = this.BackgroundImage.Size;
Make it Available from Static MethodsBecause the splash screen will only need a single instance, you can simplify your code by using static methods to access it. By just referencing the SplashScreen project, a component can launch, update or close the splash screen without needing an object reference. Add the following code to SplashScreen.cs:static SplashScreen ms_frmSplash = null;
// A static entry point to launch SplashScreen.
static public void ShowForm()
{
ms_frmSplash = new SplashScreen();
Application.Run(ms_frmSplash);
}
// A static method to close the SplashScreen
static public void CloseForm()
{
ms_frmSplash.Close();
}
Put it on its Own ThreadA splash screen displays information about your application while it is loading and initializing its components. If you are going to display any dynamic information during that time, you should put it on a separate thread to prevent it from freezing when initialization is hogging the main thread. Generally speaking, you can safely update data in cross-thread method calls, but you cannot update the UI without using Invoke to call the updating methods. In this solution, we will use Invoke for launching the Splash Screen and limit all other calls to updating instance data. A timer (which we need anyway for other effects) will update the UI from the modified data.Start by using the Threading namespace: using System.Threading;
Declare a static variable to hold the thread: static Thread ms_oThread = null;
Now add a method to create and launch the splash screen on its own thread: static public void ShowSplashScreen()
{
// Make sure it is only launched once.
if( ms_frmSplash != null )
return;
ms_oThread = new Thread( new ThreadStart(SplashScreen.ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.ApartmentState = ApartmentState.STA;
ms_oThread.Start();
}
Now ShowForm() can be made private, since the form will now be shown using ShowSplashScreen() . // A static entry point to launch SplashScreen.
static private void ShowForm()
Add Code to Fade In and Fade OutIt can add real flair to your splash screen by having it fade in when it first appears, and fade out just as your application appears. The form's Opacity property makes this easy. Declare variables defining increment and decrement rate. These define how quickly the form appears and disappears. They are directly related to the timer interval, since they represent how much the Opacity increases or decreases per timer tick, so if you modify the timer interval, you will want to change these proportionally. Private double m_dblOpacityIncrement = .05;
private double m_dblOpacityDecrement = .1;
private const int TIMER_INTERVAL = 50;
Add a timer to the form and then modify the constructor to start the timer and initialize the opacity to zero. this.Opacity = .0;
timer1.Interval = TIMER_INTERVAL;
timer1.Start();
Modify the static public void CloseForm()
{
if( ms_frmSplash != null )
{
// Make it start going away.
ms_frmSplash.m_dblOpacityIncrement = -ms_frmSplash.m_dblOpacityDecrement;
}
ms_oThread = null; // we do not need these any more.
ms_frmSplash = null;
}
Add a Tick event handler to change the opacity as the form is fading in or fading out, and to close the splash screen form when the opacity reaches 0. private void timer1_Tick(object sender, System.EventArgs e)
{
if( m_dblOpacityIncrement > 0 )
{
if( this.Opacity < 1 )
this.Opacity += m_dblOpacityIncrement;
}
else
{
if( this.Opacity > 0 )
this.Opacity += m_dblOpacityIncrement;
else
this.Close();
}
}
At this point, you have a splash screen that fades into view when you call the Add Code to Display a Status StringNow that the basic splash screen is complete, we can add status information to the form, so the user can tell that something's going on. To do this, we add the member variable private string m_sStatus;
...
// A static method to set the status.
static public string SetStatus(string newStatus)
{
if( ms_frmSplash == null )
return;
ms_frmSplash.m_sStatus = newStatus;
}
Now we modify the lblStatus.Text = m_sStatus;
Now Add a Progress BarThere's no reason to use the standard WinForms progress bar here unless you really want that look. We'll make a gradient progress bar by painting our own Panel control. To do this, add a panel named Declare a variable to hold the percent completion value. It is a double with a value that will vary between 0 and 1 as the progress bar progresses. Also declare a rectangle to hold the current progress rectangle. private double m_dblCompletionFraction = 0;
private Rectangle m_rProgress;
For now, add a public property for setting the current percent complete. Later, when we add the self-calibration feature, we'll eliminate the need for it. // Static method for updating the progress percentage.
static public double Progress
{
get
{
if( ms_frmSplash != null )
return ms_frmSplash.m_dblCompletionFraction;
return 100.0;
}
set
{
if( ms_frmSplash != null )
ms_frmSplash.m_dblCompletionFraction = value;
}
}
Now we modify the timer's Tick event handler to invalidate the portion of the Panel we want to paint. ...
int width = (int)Math.Floor(pnlStatus.ClientRectangle.Width
* m_dblCompletionFraction);
int height = pnlStatus.ClientRectangle.Height;
int x = pnlStatus.ClientRectangle.X;
int y = pnlStatus.ClientRectangle.Y;
if( width > 0 && height > 0 )
{
m_rProgress = new Rectangle( x, y, width, height);
pnlStatus.Invalidate(m_rProgress);
}
...
Finally, add a Panel control named // Paint the portion of the panel invalidated during the tick event.
private void pnlStatus_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
if( e.ClipRectangle.Width > 0 && m_iActualTicks > 1 )
{
LinearGradientBrush brBackground =
new LinearGradientBrush(m_rProgress,
Color.FromArgb(50, 50, 200),
Color.FromArgb(150, 150, 255),
LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brBackground, m_rProgress);
}
}
Smooth the Progress by Extrapolating Between Progress UpdatesI don't know about you, but I've always been annoyed by the way progress bars progress. They're jumpy, stop during long operations, and always cause me vague anxiety that maybe they've stopped responding. Well, this next bit of code tries to alleviate that anxiety by making the progress bar move even during lengthy operations. We do this by changing the meaning of the Add member variables to represent the previous progress and the amount to increment the progress bar per timer tick. private double m_dblLastCompletionFraction = 0.0;
private double m_dblPBIncrementPerTimerInterval = .0015;
Modify the Progress property to save the previous value before setting the new Progress value. ms_frmSplash.m_dblLastCompletionFraction =
ms_frmSplash.m_dblCompletionFraction;
Modify the if( m_dblLastCompletionFraction < m_dblCompletionFraction )
{
m_dblLastCompletionFraction += m_dblPBIncrementPerTimerInterval;
int width = (int)Math.Floor(pnlStatus.ClientRectangle.Width
* m_dblLastCompletionFraction);
int height = pnlStatus.ClientRectangle.Height;
int x = pnlStatus.ClientRectangle.X;
int y = pnlStatus.ClientRectangle.Y;
if( width > 0 && height > 0 )
{
pnlStatus.Invalidate(new Rectangle( x, y, width, height));
}
}
Now Make the Progress Bar Calibrate ItselfWe can now eliminate the need to specify the progress percentages by calculating the values and remembering them between splash screen invocations. Notice that this will work only if you make a fixed sequence of calls to Registry AccessFor completeness, we'll define a simple utility class for accessing the registry. You can replace this with whatever persistent string storage mechanisms you use in your application. In the source code provided, this class appears below the Don't forget to update the registry key strings to reflect your application name and company name! using Microsoft.Win32;
...
/// A class for managing registry access.
public class RegistryAccess
{
private const string SOFTWARE_KEY = "Software";
private const string COMPANY_NAME = "MyCompany";
private const string APPLICATION_NAME = "MyApplication";
// Method for retrieving a Registry Value.
static public string GetStringRegistryValue(string key,
string defaultValue)
{
RegistryKey rkCompany;
RegistryKey rkApplication;
rkCompany = Registry.CurrentUser
.OpenSubKey(SOFTWARE_KEY, false)
.OpenSubKey(COMPANY_NAME, false);
if( rkCompany != null )
{
rkApplication = rkCompany.OpenSubKey(APPLICATION_NAME, true);
if( rkApplication != null )
{
foreach(string sKey in rkApplication.GetValueNames())
{
if( sKey == key )
{
return (string)rkApplication.GetValue(sKey);
}
}
}
}
return defaultValue;
}
// Method for storing a Registry Value.
static public void SetStringRegistryValue(string key, string stringValue)
{
RegistryKey rkSoftware;
RegistryKey rkCompany;
RegistryKey rkApplication;
rkSoftware = Registry.CurrentUser.OpenSubKey(SOFTWARE_KEY, true);
rkCompany = rkSoftware.CreateSubKey(COMPANY_NAME);
if( rkCompany != null )
{
rkApplication = rkCompany.CreateSubKey(APPLICATION_NAME);
if( rkApplication != null )
{
rkApplication.SetValue(key, stringValue);
}
}
}
}
Member VariablesNow declare variables for keeping track of how long each interval between updates is taking (this time) and what it took per interval last time (from the registry). Declare registry key constants and a Boolean flag to indicate whether this is the first launch. private bool m_bFirstLaunch = false;
private DateTime m_dtStart;
private bool m_bDTSet = false;
private int m_iIndex = 1;
private int m_iActualTicks = 0;
private ArrayList m_alPreviousCompletionFraction;
private ArrayList m_alActualTimes = new ArrayList();
private const string REG_KEY_INITIALIZATION = "Initialization";
private const string REGVALUE_PB_MILISECOND_INCREMENT = "Increment";
private const string REGVALUE_PB_PERCENTS = "Percents";
Reference PointsWe need to declare methods for recording various reference points during application startup. Reference points are critical to making a self-calibrating progress bar since they replace progress bar percent-complete updates. To make the best use of this capability, you should sprinkle reference points inside of the initialization code that runs during application startup. The more you place, the smoother and more accurate your progress bar will be. This is when static access really pays off, because you don't have to pass a reference to First, we'll need a simple utility function to return elapsed Milliseconds since the Splash Screen first appeared. This is used for calculating the percentage of overall time allocated to each interval between ReferencePoint calls. // Utility function to return elapsed Milliseconds since the
// SplashScreen was launched.
private double ElapsedMilliSeconds()
{
TimeSpan ts = DateTime.Now - m_dtStart;
return ts.TotalMilliseconds;
}
// Static method called from the initializing application to
// give the splash screen reference points. Not needed if
// you are using a lot of status strings.
static public void SetReferencePoint()
{
if( ms_frmSplash == null )
return;
ms_frmSplash.SetReferenceInternal();
}
// Internal method for setting reference points.
private void SetReferenceInternal()
{
if( m_bDTSet == false )
{
m_bDTSet = true;
m_dtStart = DateTime.Now;
ReadIncrements();
}
double dblMilliseconds = ElapsedMilliSeconds();
m_alActualTimes.Add(dblMilliseconds);
m_dblLastCompletionFraction = m_dblCompletionFraction;
if( m_alPreviousCompletionFraction != null
&& m_iIndex < m_alPreviousCompletionFraction.Count )
m_dblCompletionFraction = (double)m_alPreviousCompletionFraction[
m_iIndex++];
else
m_dblCompletionFraction = ( m_iIndex > 0 )? 1: 0;
}
The next two functions, // Function to read the checkpoint intervals from the
// previous invocation of the
// splashscreen from the registry.
private void ReadIncrements()
{
string sPBIncrementPerTimerInterval = GetStringRegistryValue(
REGVALUE_PB_MILISECOND_INCREMENT, "0.0015");
double dblResult;
if( Double.TryParse( sPBIncrementPerTimerInterval,
System.Globalization.NumberStyles.Float,
System.Globalization.NumberFormatInfo.InvariantInfo,
out dblResult) )
m_dblPBIncrementPerTimerInterval = dblResult;
else
m_dblPBIncrementPerTimerInterval = .0015;
string sPBPreviousPctComplete = GetStringRegistryValue(
REGVALUE_PB_PERCENTS, "" );
if( sPBPreviousPctComplete != "" )
{
string [] aTimes = sPBPreviousPctComplete.Split(null);
m_alPreviousCompletionFraction = new ArrayList();
for(int i = 0; i < aTimes.Length; i++ )
{
double dblVal;
if( Double.TryParse(aTimes[i],
System.Globalization.NumberStyles.Float,
System.Globalization.NumberFormatInfo.InvariantInfo,
out dblVal) )
m_alPreviousCompletionFraction.Add(dblVal);
else
m_alPreviousCompletionFraction.Add(1.0);
}
}
else
{
// If this is the first launch, flag it so we don't try to
// show the scroll bar.
m_bFirstLaunch = true;
}
}
// Method to store the intervals (in percent complete)
// from the current invocation of
// the splash screen to the registry.
private void StoreIncrements()
{
string sPercent = "";
double dblElapsedMS = ElapsedMilliSeconds();
for( int i = 0; i < m_alActualTimes.Count; i++ )
sPercent += ((double)m_alActualTimes[i]/dblElapsedMS).ToString(
"0.####", System.Globalization.NumberFormatInfo.InvariantInfo) + " ";
SetStringRegistryValue( REGVALUE_PB_PERCENTS, sPercent );
m_dblPBIncrementPerTimerInterval = 1.0/(double)m_iActualTicks;
SetStringRegistryValue( REGVALUE_PB_MILISECOND_INCREMENT,
m_dblPBIncrementPerTimerInterval.ToString("#.000000",
System.Globalization.NumberFormatInfo.InvariantInfo));
}
We now can modify the static public void SetStatus(string newStatus)
{
SetStatus(newStatus, true);
}
static public void SetStatus(string newStatus, bool setReference)
{
if( ms_frmSplash == null )
return;
ms_frmSplash.m_sStatus = newStatus;
if( setReference )
ms_frmSplash.SetReferenceInternal();
}
We also need to modify the timer tick and progress bar paint event handlers to paint only when ...
// Timer1_Tick()
if( m_bFirstLaunch == false && m_dblLastCompletionFraction
< m_dblCompletionFraction )
...
//pnlStatus_Paint()
if( m_bFirstLaunch == false && e.ClipRectangle.Width > 0
&& m_iActualTicks > 1 )
Add a Time Remaining CounterFinally, we can fairly accurately estimate the remaining time for initialization by examining what percentage is yet to be done. Add a label calledlblTimeRemaining to the splash screen form to display it. Add the following code to the timer1_Tick() event handler to update the lblTimeRemaining label on the SplashScreen form. int iSecondsLeft = 1 + (int)(TIMER_INTERVAL *
((1.0 - m_dblLastCompletionFraction)/m_dblPBIncrementPerTimerInterval))
/ 1000;
if( iSecondsLeft == 1 )
lblTimeRemaining.Text = string.Format( "1 second remaining");
else
lblTimeRemaining.Text = string.Format( "{0} seconds remaining",
iSecondsLeft);
Using the SplashScreenTo use the splash screen, just call You may want to play around with the various constants to adjust the time of fade in and fade out. If you set the interval to a very short time (like 10 ms), you'll get a beautiful smoothly progressing progress bar but your performance may suffer. When the application first loads, you will notice that the progress bar and time remaining counter do not display. This is because the splash screen needs one load to calibrate the progress bar. It will appear on subsequent application launches. SplashScreen.cs Source Codeusing System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using Microsoft.Win32;
namespace SplashScreen
{
/// Summary description for SplashScreen.
public class SplashScreen : System.Windows.Forms.Form
{
// Threading
static SplashScreen ms_frmSplash = null;
static Thread ms_oThread = null;
// Fade in and out.
private double m_dblOpacityIncrement = .05;
private double m_dblOpacityDecrement = .08;
private const int TIMER_INTERVAL = 50;
// Status and progress bar
private string m_sStatus;
private double m_dblCompletionFraction = 0;
private Rectangle m_rProgress;
// Progress smoothing
private double m_dblLastCompletionFraction = 0.0;
private double m_dblPBIncrementPerTimerInterval = .015;
// Self-calibration support
private bool m_bFirstLaunch = false;
private DateTime m_dtStart;
private bool m_bDTSet = false;
private int m_iIndex = 1;
private int m_iActualTicks = 0;
private ArrayList m_alPreviousCompletionFraction;
private ArrayList m_alActualTimes = new ArrayList();
private const string REG_KEY_INITIALIZATION = "Initialization";
private const string REGVALUE_PB_MILISECOND_INCREMENT = "Increment";
private const string REGVALUE_PB_PERCENTS = "Percents";
private System.Windows.Forms.Label lblStatus;
private System.Windows.Forms.Label lblTimeRemaining;
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Panel pnlStatus;
private System.ComponentModel.IContainer components;
/// Constructor
public SplashScreen()
{
InitializeComponent();
this.Opacity = .00;
timer1.Interval = TIMER_INTERVAL;
timer1.Start();
this.ClientSize = this.BackgroundImage.Size;
}
/// Clean up any resources being used.
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Resources.ResourceManager resources =
new System.Resources.ResourceManager(typeof(SplashScreen));
this.lblStatus = new System.Windows.Forms.Label();
this.pnlStatus = new System.Windows.Forms.Panel();
this.lblTimeRemaining = new System.Windows.Forms.Label();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// lblStatus
//
this.lblStatus.BackColor = System.Drawing.Color.Transparent;
this.lblStatus.Location = new System.Drawing.Point(152, 116);
this.lblStatus.Name = "lblStatus";
this.lblStatus.Size = new System.Drawing.Size(237, 14);
this.lblStatus.TabIndex = 0;
//
// pnlStatus
//
this.pnlStatus.BackColor = System.Drawing.Color.Transparent;
this.pnlStatus.Location = new System.Drawing.Point(152, 138);
this.pnlStatus.Name = "pnlStatus";
this.pnlStatus.Size = new System.Drawing.Size(237, 24);
this.pnlStatus.TabIndex = 1;
this.pnlStatus.Paint +=
new System.Windows.Forms.PaintEventHandler(this.pnlStatus_Paint);
//
// lblTimeRemaining
//
this.lblTimeRemaining.BackColor = System.Drawing.Color.Transparent;
this.lblTimeRemaining.Location = new System.Drawing.Point(152, 169);
this.lblTimeRemaining.Name = "lblTimeRemaining";
this.lblTimeRemaining.Size = new System.Drawing.Size(237, 16);
this.lblTimeRemaining.TabIndex = 2;
this.lblTimeRemaining.Text = "Time remaining";
//
// timer1
//
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// SplashScreen
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.BackColor = System.Drawing.Color.LightGray;
this.BackgroundImage = ((System.Drawing.Image)(
resources.GetObject("$this.BackgroundImage")));
this.ClientSize = new System.Drawing.Size(419, 231);
this.Controls.Add(this.lblTimeRemaining);
this.Controls.Add(this.pnlStatus);
this.Controls.Add(this.lblStatus);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "SplashScreen";
this.StartPosition =
System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "SplashScreen";
this.DoubleClick += new System.EventHandler(
this.SplashScreen_DoubleClick);
this.ResumeLayout(false);
}
#endregion
// ************* Static Methods *************** //
// A static method to create the thread and
// launch the SplashScreen.
static public void ShowSplashScreen()
{
// Make sure it is only launched once.
if( ms_frmSplash != null )
return;
ms_oThread = new Thread( new ThreadStart(SplashScreen.ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.ApartmentState = ApartmentState.STA;
ms_oThread.Start();
}
// A property returning the splash screen instance
static public SplashScreen SplashForm
{
get
{
return ms_frmSplash;
}
}
// A private entry point for the thread.
static private void ShowForm()
{
ms_frmSplash = new SplashScreen();
Application.Run(ms_frmSplash);
}
// A static method to close the SplashScreen
static public void CloseForm()
{
if( ms_frmSplash != null && ms_frmSplash.IsDisposed == false )
{
// Make it start going away.
ms_frmSplash.m_dblOpacityIncrement = -
ms_frmSplash.m_dblOpacityDecrement;
}
ms_oThread = null; // we do not need these any more.
ms_frmSplash = null;
}
// A static method to set the status and update the reference.
static public void SetStatus(string newStatus)
{
SetStatus(newStatus, true);
}
// A static method to set the status and optionally update the reference.
// This is useful if you are in a section of code that has a variable
// set of status string updates. In that case, don't set the reference.
static public void SetStatus(string newStatus, bool setReference)
{
if( ms_frmSplash == null )
return;
ms_frmSplash.m_sStatus = newStatus;
if( setReference )
ms_frmSplash.SetReferenceInternal();
}
// Static method called from the initializing application to
// give the splash screen reference points. Not needed if
// you are using a lot of status strings.
static public void SetReferencePoint()
{
if( ms_frmSplash == null )
return;
ms_frmSplash.SetReferenceInternal();
}
// ************ Private methods ************
// Internal method for setting reference points.
private void SetReferenceInternal()
{
if( m_bDTSet == false )
{
m_bDTSet = true;
m_dtStart = DateTime.Now;
ReadIncrements();
}
double dblMilliseconds = ElapsedMilliSeconds();
m_alActualTimes.Add(dblMilliseconds);
m_dblLastCompletionFraction = m_dblCompletionFraction;
if( m_alPreviousCompletionFraction != null
&& m_iIndex < m_alPreviousCompletionFraction.Count )
m_dblCompletionFraction =
(double)m_alPreviousCompletionFraction[m_iIndex++];
else
m_dblCompletionFraction = ( m_iIndex > 0 )? 1: 0;
}
// Utility function to return elapsed Milliseconds since the
// SplashScreen was launched.
private double ElapsedMilliSeconds()
{
TimeSpan ts = DateTime.Now - m_dtStart;
return ts.TotalMilliseconds;
}
// Function to read the checkpoint intervals
// from the previous invocation of the
// splashscreen from the registry.
private void ReadIncrements()
{
string sPBIncrementPerTimerInterval =
RegistryAccess.GetStringRegistryValue(
REGVALUE_PB_MILISECOND_INCREMENT, "0.0015");
double dblResult;
if( Double.TryParse(sPBIncrementPerTimerInterval,
System.Globalization.NumberStyles.Float,
System.Globalization.NumberFormatInfo.InvariantInfo,
out dblResult) )
m_dblPBIncrementPerTimerInterval = dblResult;
else
m_dblPBIncrementPerTimerInterval = .0015;
string sPBPreviousPctComplete = RegistryAccess.GetStringRegistryValue(
REGVALUE_PB_PERCENTS, "" );
if( sPBPreviousPctComplete != "" )
{
string [] aTimes = sPBPreviousPctComplete.Split(null);
m_alPreviousCompletionFraction = new ArrayList();
for(int i = 0; i < aTimes.Length; i++ )
{
double dblVal;
if( Double.TryParse(aTimes[i],
System.Globalization.NumberStyles.Float,
System.Globalization.NumberFormatInfo.InvariantInfo,
out dblVal) )
m_alPreviousCompletionFraction.Add(dblVal);
else
m_alPreviousCompletionFraction.Add(1.0);
}
}
else
{
m_bFirstLaunch = true;
lblTimeRemaining.Text = "";
}
}
// Method to store the intervals (in percent complete)
// from the current invocation of
// the splash screen to the registry.
private void StoreIncrements()
{
string sPercent = "";
double dblElapsedMilliseconds = ElapsedMilliSeconds();
for( int i = 0; i < m_alActualTimes.Count; i++ )
sPercent += ((double)m_alActualTimes[i]/
dblElapsedMilliseconds).ToString("0.####",
System.Globalization.NumberFormatInfo.InvariantInfo) + " ";
RegistryAccess.SetStringRegistryValue(
REGVALUE_PB_PERCENTS, sPercent );
m_dblPBIncrementPerTimerInterval = 1.0/(double)m_iActualTicks;
RegistryAccess.SetStringRegistryValue(
REGVALUE_PB_MILISECOND_INCREMENT,
m_dblPBIncrementPerTimerInterval.ToString("#.000000",
System.Globalization.NumberFormatInfo.InvariantInfo));
}
//********* Event Handlers ************
// Tick Event handler for the Timer control.
// Handle fade in and fade out. Also
// handle the smoothed progress bar.
private void timer1_Tick(object sender, System.EventArgs e)
{
lblStatus.Text = m_sStatus;
if( m_dblOpacityIncrement > 0 )
{
m_iActualTicks++;
if( this.Opacity < 1 )
this.Opacity += m_dblOpacityIncrement;
}
else
{
if( this.Opacity > 0 )
this.Opacity += m_dblOpacityIncrement;
else
{
StoreIncrements();
this.Close();
}
}
if( m_bFirstLaunch == false && m_dblLastCompletionFraction
< m_dblCompletionFraction )
{
m_dblLastCompletionFraction += m_dblPBIncrementPerTimerInterval;
int width = (int)Math.Floor(
pnlStatus.ClientRectangle.Width * m_dblLastCompletionFraction);
int height = pnlStatus.ClientRectangle.Height;
int x = pnlStatus.ClientRectangle.X;
int y = pnlStatus.ClientRectangle.Y;
if( width > 0 && height > 0 )
{
m_rProgress = new Rectangle( x, y, width, height);
pnlStatus.Invalidate(m_rProgress);
int iSecondsLeft = 1 + (int)(TIMER_INTERVAL *
((1.0 - m_dblLastCompletionFraction)/
m_dblPBIncrementPerTimerInterval)) / 1000;
if( iSecondsLeft == 1 )
lblTimeRemaining.Text = string.Format( "1 second remaining");
else
lblTimeRemaining.Text = string.Format( "{0} seconds remaining",
iSecondsLeft);
}
}
}
// Paint the portion of the panel invalidated during the tick event.
private void pnlStatus_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
if( m_bFirstLaunch == false && e.ClipRectangle.Width > 0
&& m_iActualTicks > 1 )
{
LinearGradientBrush brBackground =
new LinearGradientBrush(m_rProgress,
Color.FromArgb(100, 100, 100),
Color.FromArgb(150, 150, 255),
LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brBackground, m_rProgress);
}
}
// Close the form if they double click on it.
private void SplashScreen_DoubleClick(object sender, System.EventArgs e)
{
CloseForm();
}
}
/// A class for managing registry access.
public class RegistryAccess
{
private const string SOFTWARE_KEY = "Software";
private const string COMPANY_NAME = "MyCompany";
private const string APPLICATION_NAME = "MyApplication";
// Method for retrieving a Registry Value.
static public string GetStringRegistryValue(string key,
string defaultValue)
{
RegistryKey rkCompany;
RegistryKey rkApplication;
rkCompany = Registry.CurrentUser.OpenSubKey(SOFTWARE_KEY,
false).OpenSubKey(COMPANY_NAME, false);
if( rkCompany != null )
{
rkApplication = rkCompany.OpenSubKey(APPLICATION_NAME, true);
if( rkApplication != null )
{
foreach(string sKey in rkApplication.GetValueNames())
{
if( sKey == key )
{
return (string)rkApplication.GetValue(sKey);
}
}
}
}
return defaultValue;
}
// Method for storing a Registry Value.
static public void SetStringRegistryValue(string key,
string stringValue)
{
RegistryKey rkSoftware;
RegistryKey rkCompany;
RegistryKey rkApplication;
rkSoftware = Registry.CurrentUser.OpenSubKey(SOFTWARE_KEY, true);
rkCompany = rkSoftware.CreateSubKey(COMPANY_NAME);
if( rkCompany != null )
{
rkApplication = rkCompany.CreateSubKey(APPLICATION_NAME);
if( rkApplication != null )
{
rkApplication.SetValue(key, stringValue);
}
}
}
}
}
|