|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionAwhile back, when I first browsed around for splash screen examples, I couldn't find anything that met my requirements for a splash screen. I wanted something simple, flexible, reliable, intrinsically safe -- no threads or child controls etc. -- and that would start up immediately, stay on top, go away when required and also reappear if wanted. I have noticed there are some good examples based entirely within .NET using Forms, whereas the approach I have taken uses the System.Runtime.InteropServices to create a topmost top-level window from a Control-based class. I prefer this approach, as I have found it to be reliable and very flexible with regards to customisation. Because of the required simplicity, the control handles its own painting, which provides the freedom to use the GDI to create an application-personalised splash screen. BackgroundI had been developing an application that required a splash screen. The motivation to provide a splash screen is given by any delay between launching the application and the user interface subsequently becoming available. There's something reassuring about an application that starts up straightaway, without a splash screen, ready to use. However, if a program has any reason to initialize before starting, which implies a potential for delay or failure, then it would be best to present something right away via a splash screen with a commentary. This can have its benefits, as well: A branding opportunityWith a visually pleasing background graphic for your banner, you can immediately create a better first impression with your users. It's an opportunity to brand your software and, just as importantly, brand a version. Users of Office, for instance, know immediately what version they are starting up, 2003 or 2007, for example. A moment of greater user attentionIt's a good opportunity to take advantage of the increased attention you have from your user. After all, they've just thought about your software and launched it. So whilst loading you could, for example, remind them to visit your website for up-to-date news about your products. A chance to relay start-up and configuration informationIf your program relies on establishing connections -- or dynamically loads plug-ins or the like -- then a splash screen can make the user aware of what you are connecting to or loading. This information could prove useful to both you and your users. Using the CodeTo use the class
After the calls to the application visual styles and text rendering settings, initialize // program.cs
...
using SplashScreenControl;namespace WindowsApplication1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
// the following demonstrates how to get hold of the
// instance object and change control properties
SplashScreen.Instance.Font =
new System.Drawing.Font( "Verdana", 11.75F,
System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point, ((byte)(0)) );
// begin displaying the splash screen before running the
// application form
SplashScreen.SetBackgroundImage(
WindowsApplication1.Resources.splashbg );
SplashScreen.SetTitleString( "SplashScreen Demo" );
SplashScreen.BeginDisplay();
// run the application
Application.Run( new Form1() );
}
}
}
//
The SplashScreen ClassThe // splashscreen.cs
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TOOLWINDOW| WS_EX_TOPMOST;
cp.Parent = IntPtr.Zero;
return cp;
}
}
//
Next, we override // splashscreen.cs
protected override void OnHandleCreated(EventArgs e)
{
if ( this.Handle != IntPtr.Zero )
{
IntPtr hWndDeskTop = GetDesktopWindow();
SetParent( this.Handle, hWndDeskTop );
}
base.OnHandleCreated(e);
}
//
This is enough to get the control on top for the start-up situation, but at this point it is only a top-level window. When the instance is created, it will become the topmost top-level window. If we want to display the splash again, and in the meantime have had another top-level window go topmost (Task Manager with Options -> Always On Top set to True, for example), then the control needs its topmost attribute set again. This is done using the The Paint RoutinesThe work of painting the splash screen is shared by two overrides, // splashscreen.cs
protected override void OnPaintBackground( PaintEventArgs e )
{
if ( Bounds.Width > 0 && Bounds.Height > 0 && Visible )
{
try
{
Rectangle rect =
new Rectangle(0, 0, Bounds.Width, Bounds.Height);
Graphics g = e.Graphics;
g.SetClip(e.ClipRectangle);
if (BackgroundImage == null)
{
SolidBrush solidBrush = new SolidBrush(BackColor);
g.FillRectangle(solidBrush, rect);
solidBrush.Dispose();
}
else
{
g.DrawImage(BackgroundImage, rect, 0, 0,
BackgroundImage.Width, BackgroundImage.Height,
GraphicsUnit.Pixel);
}
}
catch (Exception exception)
{
System.Diagnostics.StackFrame stackFrame =
new System.Diagnostics.StackFrame(true);
Console.WriteLine(
"\nException: {0}, \n\t{1}, \n\t{2}, \n\t{3}\n",
this.GetType().ToString(), stackFrame.GetMethod().ToString(),
stackFrame.GetFileLineNumber(), exception.Message);
}
}
}
//
During // splashscreen.cs
protected override void OnPaint( PaintEventArgs e )
{
if ( Bounds.Width > 0 && Bounds.Height > 0 && Visible )
{
try
{
Graphics g = e.Graphics;
g.SetClip(e.ClipRectangle);
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
float nClientHeight = ClientRectangle.Height;
//start the text two thirds down:
m_nTextOffsetY =
Convert.ToInt32(Math.Ceiling(((nClientHeight / 3) * 2))) +
m_nLeading;
if (TitleString != string.Empty)
{
Font fontTitle = new Font(Font, FontStyle.Bold);
SizeF sizeF = g.MeasureString(TitleString, fontTitle,
ClientRectangle.Width, m_stringFormat);
m_nTextOffsetY += Convert.ToInt32(
Math.Ceiling(sizeF.Height));
RectangleF rectangleF = new RectangleF(
ClientRectangle.Left + m_nTextOffsetX,
ClientRectangle.Top + m_nTextOffsetY, sizeF.Width,
sizeF.Height);
SolidBrush brushFont = new SolidBrush(ForeColor);
g.DrawString(TitleString, fontTitle, brushFont,
rectangleF, m_stringFormat);
brushFont.Dispose();
fontTitle.Dispose();
m_nTextOffsetY += m_nLeading;
}
if (CommentaryString != string.Empty)
{
SizeF sizeF = g.MeasureString(CommentaryString, Font,
ClientRectangle.Width, m_stringFormat);
m_nTextOffsetY += Convert.ToInt32(
Math.Ceiling(sizeF.Height));
RectangleF rectangleF =
new RectangleF(ClientRectangle.Left + m_nTextOffsetX,
ClientRectangle.Top + m_nTextOffsetY, sizeF.Width,
sizeF.Height);
SolidBrush brushFont = new SolidBrush(ForeColor);
g.DrawString(CommentaryString, Font, brushFont,
rectangleF, m_stringFormat);
brushFont.Dispose();
}
}
catch (Exception exception)
{
System.Diagnostics.StackFrame stackFrame =
new System.Diagnostics.StackFrame(true);
Console.WriteLine("\nException: {0}, \n\t{1}, \n\t{2},
\n\t{3}\n", this.GetType().ToString(),
stackFrame.GetMethod().ToString(),
stackFrame.GetFileLineNumber(), exception.Message);
}
}
}
//
Points of InterestThe control handles its painting routines based upon the static methods HistoryVersion 1.0.0 Released: 24th October 2007
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||