[转]SplashScreen实现加载数据

背景
对于windows开发人员来说在打开VS开发工具时,总是先呈现一个SplashScreen界面,登上几秒钟后才打开VS的主界面。这样的效果一般是在主界面需要加载大量资源,为避免主界面变成“死”界面,而提供一个友好的Loading画面。为实现该效果,我们通常在加载主界面Application.Run(new MainForm())之前打开一个SplashScreen窗口,并在SplashScreen窗口中加载数据。

接口WindowsFormsApplicationBase
微软提供了WindowsFormsApplicationBase类,该类提供了SplashScreen属性,及OnCreateSplashScreen虚方法的接口。在实现自己的SplashScreen窗口时,主要重载OnCreateSplashScreen方法,并创建一个Form对象,赋值给SplashScreen属性,并且该类还提供了MinimumSplashScreenDisplayTime属性,用于设置SplashScreen窗口的呈现时间。当然你可以自己控制SplashScreen窗口的呈现和关闭。

实现Application类
首先我们需要实现WindowsFormsApplicationBase基类SplashScreenApplication,并重新OnCreateSplashScreen方法。代码如下:

internal sealed class SplashScreenApplication : WindowsFormsApplicationBase {
        public SplashScreenApplication() {
            base.IsSingleInstance = true;
            base.EnableVisualStyles = true;
     base.MinimumSplashScreenDisplayTime = 2000;
        }

        protected override void OnCreateMainForm() {
            this.MainForm = new Form1();
        }

        protected override void OnCreateSplashScreen() {
            this.SplashScreen = new SplashScreenForm();
        }
    }
其中Form1为主窗口,SplashScreenForm为Loading窗口,并设置2000毫秒SplashScreen自动关闭,并试图打开主窗体。Application会首先执行OnCreateSplashScreen方法,然后执行OnCreateMainForm窗口。需要注意的是,2000毫秒并不是两个方法的执行的时间间隔,而是主窗体创建2000毫秒后才关闭SplashScreen窗体,并显示主窗体,这里的SplashScreenForm不能用于加载数据,因为2000毫秒好就会关闭,我们不能保证SplashScreen可以在2000毫秒吧数据加载完成。

实现加载效果
这里SplashScreen的目的是加载数据,而不是简单的友好效果,因此简单的2000毫秒不能达到我们加载数据的需求。鉴于此,我们需要自己控制SplashScreen界面,当数据加载完成后才能关闭SplashScreen窗口,并显示主界面。为达到加载数据的效果,这里会再SpashScreen界面显示加载数据的过程。代码如下:

internal sealed class SplashScreenApplication : WindowsFormsApplicationBase {
        public ManualResetEvent resetEvent = new ManualResetEvent(true);

        public SplashScreenApplication() {
            base.IsSingleInstance = true;
            base.EnableVisualStyles = true;
        }

        protected override void OnCreateMainForm() {
            if (resetEvent.WaitOne()) {
                this.MainForm = new Form1();
            }
        }

        protected override void OnCreateSplashScreen() {
            this.SplashScreen = new SplashScreenForm();
        }
    }
WindowsFormsApplicationBase 类是位于Microsoft.VisualBasic.ApplicationServices命名空间下,需要添加Microsoft.VisualBasic.dll引用。为演示加载过程,SplashScreenForm还负责了加载数据:

public partial class SplashScreenForm : Form {
        static string[] Resources = new string[]{
            "Check update.",
            "Updating",
            "Downloading xxx.dll from remote server.",
            "Downloading xxx.config from remote server.",
            "Updated.",
            "Check if the devices pluged in.",
            "Open devices.",
            "Load data from database.",
            "Load file data from remote.",
            "Download necessary files.",
            "Ready"
        };

        public SplashScreenForm() {
            InitializeComponent();
            this.Opacity = 0.8;
            Program.app.resetEvent.Reset();
        }

        private void SplashScreenForm_Load(object sender, EventArgs e) {
            backgroundWorker1.RunWorkerAsync();
        }

        private delegate void run();

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
            float total = 0f;
            float pre = 100f / Resources.Length;

            foreach (string res in Resources) {
                total += pre;
                backgroundWorker1.ReportProgress((int)total, res);
                Thread.Sleep(1000);
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
            this.Invoke(new run(() => {
                label1.Text = e.UserState.ToString();
                progressBar1.Value = e.ProgressPercentage;
            }));
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            this.Invoke(new run(() => {
                label1.Text = "Inititalization finished.";
                progressBar1.Value = 100;
               
            }));

            Thread.Sleep(1000);

            this.Invoke(new run(() => {
                this.Close();
                Program.app.resetEvent.Set();
            }));
        }
    }
程序入口:
static class Program {
        public static SplashScreenApplication app = new SingleInstanceApplication();
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args) {
            app.Run(args);
        }
    }
运行效果
运行效果如下图:

 

其他
另外我们的加载数据过程只在SplashScreen窗口中,那么MainForm就不能加载太耗时的资源了,否则关闭SplashScreen窗口后,才开始创建MainForm窗体,就其不到Loading的效果了。如果希望在MainForm中加载数据的,也可以修改代码,在MainForm中控制SplashScreen的关闭。

 

原文:http://www.2cto.com/kf/201212/173941.html

posted @ 2013-03-01 16:44  安度  阅读(459)  评论(0编辑  收藏  举报