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);
這樣,一個簡單的閃屏程式就完成了。