使用ApplicationContext类来完全封装闪屏功能

http://www.codeproject.com/KB/cs/applicationcontextsplash.aspx
http://weblogs.asp.net/justin_rogers/archive/2004/04/11/111162.aspx
 
1. 简介
 
对于开发者,我们总是想要在我们的应用程序中加入一些很酷的功能。这是我们程序员血液中无法抑制的天性本能。要不然我们早就改行做审计或销售了。对于Winform应用程序,增加酷的功能的方法之一是在应用程序启动前增加闪屏功能。
 
2. Application Context(应用程序上下文):它到底干些啥?
 
我首先花点时间说一下概念性的东西。如果了解我们的继承类在背后干了些什么会对理解有很大帮助。如果你对这些不感兴趣请跳过这章。
 
每个WinForms应用程序都有一个ApplicationContext类的实例,你可能只是不知道而已。我们来看一个WinForms的标准的Main()函数:
static void Main()
{
    Application.Run(new Form1());
}
 
你肯定看到过这个。不过你知道这个函数也能写成如下的形式?:
static void Main()
{
    ApplicationContext appCtx = new ApplicationContext(new Form1());
    Application.Run(appCtx);
}
(我试过了,的确可以)
 
在前一个例子中,Application.Run函数只是创建了一个新的ApplicationContext实例,然后把Form对象传递给它的构造函数(constructor)。在后一个例子中,我们只是手动进行了这个步骤。所以从现在开始,请记住每个WinForm应用程序有一个ApplicationContext实例,它包含着Main Form的实例。ApplicationContext的目的就是作为你应用程序的Main Form和UI线程之间的应用程序启动与终结的标识链接(notification link )。UI线程是你的应用程序用户界面所在的主线程,它负责处理应用程序的消息循环。这个消息队列接收来自操作系统的事件消息,例如鼠标右键点击,空格键被按下了,并把这些消息发送给了需要处理这些消息的Form。
 
消息循环是在ThreadContext类之中。这是一个私有的子类,定义在Application类之内。关于ThreadContext的内部机理的介绍非常少,不过你可以使用Anikrino工具(我也不知道是啥,有兴趣的google吧)来深入研究。
 
让我们返回Main函数。当Main()调用了Application.Run,它接着就会调用ThreadContext.RunMessageLoop,传递到新创建的ApplicationContext实例,而这个ApplicationContext实例中包含了应用程序的Main Form实例。然后RunMessageLoop注册了ThreadContext的回叫函数OnAppThreadExit函数在ApplicationContext的ExitThread事件后触发。这就让消息循环能得知用户什么时候关闭了应用程序的Main Form。接着RunMessageLoop把Main Form的Visible属性设置为true,这样用户就能最终看到Main Form。最后RunMessageLoop的工作就是进入真实的消息循环,开始接收和处理来自操作系统的事件消息。
(好像有点乱,我来整理一下:
Main() call Application.Run
|
/
ThreadContext.RunMessageLoop
|
/
pass in ApplicationContext instance(contain main form)
|
/
ThreadContext.RunMessageLoop call ThreadContext.OnAppThreadExit
|
/
ThreadContext.RunMessageLoop set main form visible=true
|
/
ThreadContext.RunMessageLoop start actual message loop
 
ApplicationContext类只有一个属性:MainForm。在我们之前看过的Main函数范例中,我们传入ApplicationContext构造函数的Form实例设置(get set to)了MainForm属性。set_MainForm属性会注册ApplicationContext.OnMainFormDestroy回叫函数在HandleDestroyed事件(用户关闭Form)后触发。那样ApplicationContext就知道应用程序main form什么时候被销毁。
 
这个回叫对于Windows应用程序非常重要。Forms不是应用程序,他们只是Application对象保持引用的对象。如果Application对象不知道什么时候main form被销毁,那么它会永无止境地运行消息循环。因此当用户关闭了main form,form会发起它自己的HandleDestroyed事件,而这个事件会调用ApplicationContext的OnMainFormDestroy回叫函数。这个回叫函数接着发起ThreadContext的ExitThread事件,调用ThreadContext的OnAppThreadExit回叫函数。而调用这个函数告诉ThreadContext应用程序的main form已经被销毁,它可以终结UI线程了。最后把应用程序的关闭消息返回给操作系统,清空所有资源,终结UI线程。(过程真纠结。。。。)
 
3. 根据上面的概念来创建一个闪屏
 
上面有些说得太多了,不过这有助于你来理解接下来我们究竟要做些什么。
 
微软本可以不暴露出ApplicationContext类来给我们使用,但他们最终还是暴露了出来。因此我们能继承然后自定义这个启动过程。本文中我们就会创造一个自定义的ApplicationContext,包括两个Form,一个闪屏Form,一个main form。
  

另外新建一个Class SplashAppContext.cs,这个SplashAppContext类继承ApplicationContext类。
ApplicationContext有两个构造函数ApplicationContext()与ApplicationContext(Form),后者在初始一个新的ApplicationContext实例的同时还会附带一个指定的Form。如果使用了这个构造函数,可能需要重载OnMainFormClosed函数,不然线程的消息循环会在MainForm关闭时随之关闭。

代码如下:

    class SplashAppContext:ApplicationContext
    {
        Form mainForm 
= null;
        Timer splashTimer 
= new Timer();

        
public SplashAppContext(Form mainForm, Form splashForm)
            : 
base(splashForm)
        {
            
this.mainForm = mainForm;
            splashTimer.Tick 
+= new EventHandler(splashTimer_Tick);
            splashTimer.Interval 
= 10000;
            splashTimer.Enabled 
= true;
        }

        
void splashTimer_Tick(object sender, EventArgs e)
        {
            splashTimer.Enabled 
= false;
            splashTimer.Dispose();

            
base.MainForm.Close();
        }

        
protected override void OnMainFormClosed(object sender, EventArgs e)
        {
            
if (sender is Splash)
            {
                
base.MainForm = this.mainForm;
                
base.MainForm.Show();
            }
            
else if (sender is Main) 
            {
                
base.OnMainFormClosed(sender, e);
            }
        }
    }

之前提到过的Timer的知识就是应用在这个上面的,回忆不起来用法的可以参考http://www.cnblogs.com/galaxyyao/archive/2009/10/04/1577860.html。关于
public SplashAppContext(Form mainForm, Form splashForm)
            : base(splashForm)
这段,感谢renyu同学的解释。它会先运行父类的方法再运行子类的方法。因为是base(splashForm),所以会先创建一个splashForm,开启计时器splashTimer,然后创建一个mainForm。
少女计时中。。。(这是一个neta,请54)
当计时器到点,触发base.MainForm.Close(),但由于OnMainFormClosed被重载过,所以在关闭时还会检查一下是不是splashForm。如果是splashForm的话,换最终要显示的mainForm显示,然后splashForm自己dispose销毁。
最后我们打开包含Main函数的Program.cs,把Application.Run(new Main());替换成自定义的ApplicationContext:

SplashAppContext splashContext = new SplashAppContext(new Main(), new Splash());
Application.Run(splashContext);

这样,一个简单的闪屏程序就完成了。


原文地址:https://www.cnblogs.com/galaxyyao/p/1591632.html