代码改变世界

[C#]增强响应性,用加载窗体(Splash)来载入主窗体

2010-09-07 13:13 by Windie Chai, ... 阅读, ... 评论, 收藏, 编辑

许多软件在启动的时候都会显示一个加载窗口(Splash),譬如微软的Visual Studio、Office以及Adobe的许多软件。这些加载窗口很精美,但“漂亮”并不是它们的主要作用。

设想一下用户双击了一个图标,等了许久都没有看到主窗体,就会感到迷惑:是不是刚才没有点中?于是又双击了一次,这次终于看到主窗体了,但看到了两个,因为启动了两次。

如果加载主窗体需要大量时间,那么在加载主窗体的同时去显示一个加载窗体就可以让用户知道软件已经响应了指令,并且正在进行处理,还可以告诉用户当前处理的进度,从而避免了用户的迷惑和误操作。

恰巧最近我的客户也有这样的抱怨,便研究了一下加载窗体的实现方法,顺便记录在这里以免遗忘。

那么就开始编写一个加载窗体吧。

我创建了一个很简单的窗体,它只包含一个Style=Marquee的ProgressBar(这个进度条会不断滚动),下面是它的代码以及注释:

public partial class Splash : Form 
{ 
    public Splash() 
    { 
        InitializeComponent(); 
    }
    //关闭自身 
    public void KillMe(object o, EventArgs e) 
    { 
        this.Close(); 
    }
    /// <summary> 
    /// 加载并显示主窗体 
    /// </summary> 
    /// <param name="form">主窗体</param> 
    public static void LoadAndRun(Form form) 
    { 
        //订阅主窗体的句柄创建事件 
        form.HandleCreated += delegate 
        { 
            //启动新线程来显示Splash窗体 
            new Thread(new ThreadStart(delegate 
            { 
                Splash splash = new Splash(); 
                //订阅主窗体的Shown事件 
                form.Shown += delegate 
                { 
                    //通知Splash窗体关闭自身 
                    splash.Invoke(new EventHandler(splash.KillMe)); 
                    splash.Dispose(); 
                }; 
                //显示Splash窗体 
                Application.Run(splash);
            })).Start(); 
        }; 
        //显示主窗体 
        Application.Run(form); 
    }

代码很好理解,Splash类只包含两个方法:一个普通的事件处理程序KillMe和一个静态方法LoadAndRun。

LoadAndRun方法用于加载并显示主窗体。在加载主窗体的同时,Splash窗体也会一直显示,直到主窗体加载完毕可以完全显示为止。

使用此加载窗体的方法也很简单,只需要把Program.cs中Main方法里的

Application.Run(new Form1());

修改为

Splash.LoadAndRun(new Form1());

即可。

如果想要看到效果,可以在Form1的OnLoad事件中让主线程睡一会儿觉,譬如:

protected override void OnLoad(EventArgs e) 
{ 
    System.Threading.Thread.Sleep(5000); 
    base.OnLoad(e); 
}
 

为什么要在新线程中显示加载窗体呢?因为忙碌的主窗体已经占有了主线程,如果把加载窗体也安排到主线程的话,它不仅很容易变成“失去响应”的状态,而且有可能连自身都无法顺利加载完,更别说不断滚动的进度条了。

另外,这种方法还有一个缺点,如果主窗体加载缓慢是因为在构造函数中执行了大量操作的话,那么这种方法就起不到作用了。

不过话说回来,在窗体的构造函数中执行影响性能的操作本来就是不被推荐的做法,应当尽量避免。