65.9K
CodeProject 正在变化。 阅读更多。
Home

启动画面控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (27投票s)

2007年10月26日

CPOL

5分钟阅读

viewsIcon

91088

downloadIcon

5111

一个轻量级且易于使用的启动画面控件,在应用程序窗体之前启动。

Screenshot - SplashScreen.jpg

引言

很久以前,当我第一次浏览启动画面示例时,我找不到任何符合我要求的启动画面。我想要一个简单、灵活、可靠、本质安全——没有线程或子控件等——并且能够立即启动、保持在顶层、在需要时消失、并且也能在需要时再次出现的启动画面。我注意到有一些很好的示例完全基于 .NET 中的 Forms,而我采用的方法是使用 System.Runtime.InteropServices 从一个基于控件的类创建一个置顶的顶层窗口。我更喜欢这种方法,因为它已被证明是可靠的,并且在自定义方面非常灵活。由于需要简单性,控件会处理自己的绘制,这使得可以使用 GDI 创建应用程序个性化的启动画面。

背景

我一直在开发一个需要启动画面的应用程序。启动应用程序和用户界面随后可用之间的任何延迟都可以是提供启动画面的动机。

应用程序立即启动,没有启动画面,随时可用,这让人感到放心。然而,如果程序有任何理由在启动前进行初始化,这意味着可能出现延迟或失败,那么最好通过带有评论的启动画面立即显示某些内容。这也有其好处。

品牌推广机会

通过为您的横幅添加视觉上令人愉悦的背景图片,您可以立即给用户留下更好的第一印象。这是一个品牌推广您软件的机会,同样重要的是,品牌推广一个版本。例如,Office 用户可以立即知道他们启动的是哪个版本,比如 2003 或 2007。

用户关注度更高的一刻

这是一个利用用户增加关注度的绝佳机会。毕竟,他们刚刚考虑了您的软件并启动了它。因此,在加载时,您可以提醒他们访问您的网站,以获取有关您产品的最新消息。

传达启动和配置信息的机会

如果您的程序依赖于建立连接——或者动态加载插件等——那么启动画面可以告知用户您正在连接到什么或加载什么。这些信息可能对您和您的用户都很有用。

Using the Code

要使用 SplashScreen 类,请将文件 SplashScreen.cs 添加到您的应用程序中。在您的程序中找到入口点 Main,对于生成的项目,通常在 Program.cs 中。

SplashScreen 被实现为一个单例模式类。这是一个允许最多创建一个自身实例的类。这是通过使用一个 private 构造函数,其中包含一个指向已创建实例的 static 成员引用,以及对该实例的 public static 访问来实现的。SplashScreen 还提供了 public static 方法来管理控件的使用。

在调用应用程序视觉样式和文本渲染设置后,使用您的图像资源(如果需要)初始化 SplashScreen,并调用 static 方法 BeginDisplay() 来显示启动画面。从现在开始,在您的启动检查点使用 static 方法 SetTitleStringSetCommentaryString 来让用户了解进度。请记住,您在 Main 中初始化 SplashScreen,但在您的 Forms 应用程序中会继续使用它并结束它的显示。您的应用程序主窗体中主要关注的地方是 OnLoadOnShown 重写。最重要的是,您要在 OnShown 重写中调用 static 方法 EndDisplay()

// 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() );
        }
    }
}

//

SplashScreen 类

SplashScreen 类本身使用 System.Runtime.InteropServices 创建一个由桌面父级的顶层窗口。这是通过首先重写 CreateParams 属性的 Getter 并添加 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);
}

//

这样足以使控件在启动时位于顶层,但此时,它只是一个顶层窗口。当实例创建时,它将成为置顶的顶层窗口。如果我们想再次显示启动画面,并且在此期间有另一个顶层窗口成为置顶(例如,任务管理器,其选项 -> 始终置顶设置为 True),那么控件需要再次设置其置顶属性。这是通过使用 SetWindowPos Windows API 调用完成的。控件在 BeginDisplay 期间进行此调用,这确保了控件始终会从置顶开始。

绘制例程

启动画面的绘制工作由两个重写方法共享,即 OnPaintOnPaintBackground。在 OnPaintBackground 期间,控件会绘制 BackgroundImage 属性(如果已设置)或用当前的 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 期间,控件获取 TitleStringCommentaryString 的值,计算它们的大小和位置,并使用 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);
        }
    }
}

//

关注点

控件根据 static 方法 SetTitleStringSetCommentaryString 来处理其绘制例程。您可能需要不同的要求或想以其他方式处理。在这方面,我希望您发现设计让您自由这样做。只需添加您所需的属性或方法,并修改您的绘制例程来实现它们。

历史

  • 版本 1.0.0 发布:2007 年 10 月 24 日
© . All rights reserved.