简介
回首以前,我到处寻找Splash Screen的例子,不过我找不到一个符合我的需求的例子。我需要一个简单的、容易复用的、可靠的、安全的——没有线程或子控件;并且,我希望它能够在启动的时候立即出现并停留在屏幕上,在必要的时候消失,当然你可以随时让它复现。我也看到有不少基于.NET下Win Form做的例子,而,这篇文章中,我将使用System.Runtime.InteropServices创建一个继承与控件类的最顶层窗口。我非常喜欢这个方案,因为,我发现它很容易定制。
代码使用
添加SplashScreen.cs到你的程序中,到你的程序的Main函数中,一般来说这个函数在Program.cs文件里。
SplashScreen实现于Singleton模板类。通过一个私有的构造函数,和一个引用到已创建的实例的静态的成员完成...(设计模式上的概念,自己可以看看),最终目的,保证只有一个实例在同时运行。
调用方法
// program.cs ... using SplashScreenControl;namespace WindowsApplication1 { static class Program { /// /// The main entry point for the application. /// [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() ); } } }
SplashScreen类原理
该类重写了CreateParams属性,以添加WS_EX_TOPMOST属性,这会使得窗口一直在最上方。同时,还增设了WS_EX_TOOLWINDOW,这样窗口就不会出现在任务栏,并且不会被你的Alt+Tab窗口切换看到。
// 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; } }
然后,重写OnHandleCreated,设置其父窗口为你的桌面。
// splashscreen.cs protected override void OnHandleCreated(EventArgs e) { if ( this.Handle != IntPtr.Zero ) { IntPtr hWndDeskTop = GetDesktopWindow(); SetParent( this.Handle, hWndDeskTop ); } base.OnHandleCreated(e); }
当然,到目前为止看似已经完成了窗口出现在最上方。不过,如果有另外一个窗口被设置成Top-Most呢?你的窗口就不会在最上面了。所以,我们需要再一次设置topmost属性,这些通过SetWindowPos API实现,控件会在BeginDisplay中调用,以保证窗口永远是最上层的窗口。
绘图逻辑
绘图部分重写了两个函数OnPaint和OnPaintBackground。如果BackgroundImage属性设置了,那么OnPaintBackground会绘制它,否则以当前BackColor填充背景。
// 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); } } }
在OnPaint里,控件得到TitleString和CommentaryString值,计算大小和位置,使用Graphics.DrawString绘制文字。
// 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); } } }