「スプラッシュウィンドウ(Splash Window)」とは、アプリケーション起動時に真っ先に表示され、アプリケーションのロゴやバージョンなどの情報を表示し、自動的に消える(消えないものも稀にありますが)ウィンドウです。Microsoft Visual Studioや、Officeなど、多くのアプリケーションでスプラッシュウィンドウが使われています。
なお、Visual Studio 2005以降のVB.NETをお使いであれば、もっと簡単な方法があります。この方法に関しては、「VB.NETでスプラッシュウィンドウを表示する」をご覧ください。
まず、スプラッシュウィンドウに絶対必要な機能をあげてみます。
1.に関しては、エントリポイント(Mainメソッド)または、メインフォームのコンストラクタやLoadイベントハンドラなどでメインフォームが表示される前にスプラッシュウィンドウを表示するようにすればよいでしょう。2.に関してはスプラッシュウィンドウのフォームのStartPositionプロパティをCenterScreenに、3.に関してはFormBorderStyleプロパティをNoneにすればよいでしょう。
問題は4.です。どのタイミングでスプラッシュウィンドウを閉じるべきでしょうか?メインフォームが表示されるタイミングでということであれば、メインフォームのActivatedイベントで閉じるようにすればよいでしょう。また、「アプリケーションの準備ができた」ということを「アプリケーションがアイドル状態になった時」と解釈すると、Application.Idleイベントで閉じる方法も考えられます。さらに、メインフォームのLoadイベントハンドラの最後で閉じる方法もあります。起動してから指定時間までスプラッシュウィンドウを表示したいときは、タイマーを使う方法もあるかもしれません(個人的な意見としては、スプラッシュウィンドウは用が無くなったらさっさと閉じるべきだと思いますので、この方法はお勧めしたくありません)。
以上の機能以外に、場合によっては、次のような機能も必要になるでしょう。
5.については、フォームのShowInTaskbarプロパティをFalseにすればよいでしょう。6.については、フォームのTopMostプロパティをTrueにする方法があります。しかし、個人的な意見としては、TopMostにはせずに、メインウィンドウの後ろに隠れないようにAddOwnedFormメソッド等を使用するにとどめた方がよいと思います。
以上のことを踏まえ、実際にスプラッシュウィンドウを作ってみます。
まずプロジェクトに新しいフォームを追加し、名前を「SplashForm」とします。SplashFormのStartPositionプロパティをCenterScreen、FormBorderStyleプロパティをNone、さらにShowInTaskbarプロパティをFalseにします。その他、画像の配置など、SplashFormのデザインを適当に行ってください。
さらにSplashFormクラスに次のようなコードを記述します。ここではApplication.IdleイベントでSplashFormを閉じることにします。
'Splashフォーム Private Shared _form As SplashForm = Nothing ''' <summary> ''' Splashフォーム ''' </summary> Public Shared ReadOnly Property Form() As SplashForm Get Return _form End Get End Property ''' <summary> ''' Splashフォームを表示する ''' </summary> Public Shared Sub ShowSplash() If _form Is Nothing Then 'Application.IdleイベントハンドラでSplashフォームを閉じる AddHandler Application.Idle, AddressOf Application_Idle 'Splashフォームを表示する _form = New SplashForm _form.Show() End If End Sub 'アプリケーションがアイドル状態になった時 Private Shared Sub Application_Idle( _ ByVal sender As Object, ByVal e As EventArgs) 'Splashフォームがあるか調べる If Not (_form Is Nothing) And _form.IsDisposed = False Then 'Splashフォームを閉じる _form.Close() End If _form = Nothing 'Application.Idleイベントハンドラの削除 RemoveHandler Application.Idle, AddressOf Application_Idle End Sub
//Splashフォーム private static SplashForm _form = null; /// <summary> /// Splashフォーム /// </summary> public static SplashForm Form { get { return _form; } } /// <summary> /// Splashフォームを表示する /// </summary> public static void ShowSplash() { if (_form == null) { //Application.IdleイベントハンドラでSplashフォームを閉じる Application.Idle += new EventHandler(Application_Idle); //Splashフォームを表示する _form = new SplashForm(); _form.Show(); } } //アプリケーションがアイドル状態になった時 private static void Application_Idle(object sender, EventArgs e) { //Splashフォームがあるか調べる if (_form != null && _form.IsDisposed == false) { //Splashフォームを閉じる _form.Close(); } _form = null; //Application.Idleイベントハンドラの削除 Application.Idle -= new EventHandler(Application_Idle); }
SplashFormを表示するには、SplashForm.ShowSplashメソッドを呼び出します。次の例では、エントリポイントであるMainメソッドでメインフォームForm1を表示させる前にスプラッシュウィンドウを表示させています。エントリポイントの意味が分からなければ、まずこちらをご覧ください。
<STAThread()> _ Shared Sub Main() 'スプラッシュウィンドウを表示 SplashForm.ShowSplash() 'メインウィンドウを表示 Application.Run(New Form1) End Sub
[STAThread] static void Main() { //スプラッシュウィンドウを表示 SplashForm.ShowSplash(); //メインウィンドウを表示 Application.Run(new Form1()); }
次にスプラッシュウィンドウをメインスレッドとは別のスレッドで表示させるようにしてみます。
まず、スプラッシュウィンドウのフォームクラスは先の例と同様の方法で作成しておき(ここでもクラスの名前を"SplashForm"とします)、SplashFormクラス内に次のようなコードを記述してください。
'Splashフォーム Private Shared _form As SplashForm = Nothing 'メインフォーム Private Shared _mainForm As Form = Nothing 'Splashを表示するスレッド Private Shared _thread As System.Threading.Thread = Nothing 'SyncLock用のオブジェクト Private Shared ReadOnly syncObject As New Object() 'Splashが表示されるまで待機するための待機ハンドル Private Shared splashShownEvent As System.Threading.ManualResetEvent = Nothing ''' <summary> ''' Splashフォーム ''' </summary> Public Shared ReadOnly Property Form() As SplashForm Get Return _form End Get End Property ''' <summary> ''' Splashフォームを表示する ''' </summary> ''' <param name="mainForm">メインフォーム</param> Public Shared Sub ShowSplash(ByVal mainForm As Form) SyncLock syncObject If (Not _form Is Nothing) OrElse (Not _thread Is Nothing) Then Return End If _mainForm = mainForm 'メインフォームのActivatedイベントでSplashフォームを消す If Not _mainForm Is Nothing Then AddHandler _mainForm.Activated, AddressOf _mainForm_Activated End If '待機ハンドルの作成 splashShownEvent = New System.Threading.ManualResetEvent(False) 'スレッドの作成 _thread = New System.Threading.Thread( _ New System.Threading.ThreadStart(AddressOf StartThread)) _thread.Name = "SplashForm" _thread.IsBackground = True _thread.SetApartmentState(System.Threading.ApartmentState.STA) '.NET Framework 1.1以前では、以下のようにする '_thread.ApartmentState = System.Threading.ApartmentState.STA 'スレッドの開始 _thread.Start() End SyncLock End Sub ''' <summary> ''' Splashフォームを表示する ''' </summary> Public Shared Sub ShowSplash() ShowSplash(Nothing) End Sub ''' <summary> ''' Splashフォームを消す ''' </summary> Public Shared Sub CloseSplash() SyncLock syncObject If _thread Is Nothing Then Return End If If Not _mainForm Is Nothing Then RemoveHandler _mainForm.Activated, AddressOf _mainForm_Activated End If 'Splashが表示されるまで待機する If Not splashShownEvent Is Nothing Then splashShownEvent.WaitOne() splashShownEvent.Close() splashShownEvent = Nothing End If 'Splashフォームを閉じる 'Invokeが必要か調べる If Not _form Is Nothing Then If _form.InvokeRequired Then _form.Invoke(New MethodInvoker(AddressOf CloseSplashForm)) Else CloseSplashForm() End If End If 'メインフォームをアクティブにする If Not _mainForm Is Nothing Then If _mainForm.InvokeRequired Then _mainForm.Invoke(New MethodInvoker(AddressOf ActivateMainForm)) Else ActivateMainForm() End If End If _form = Nothing _thread = Nothing _mainForm = Nothing End SyncLock End Sub 'スレッドで開始するメソッド Private Shared Sub StartThread() 'Splashフォームを作成 _form = New SplashForm() 'Splashフォームをクリックして閉じられるようにする AddHandler _form.Click, AddressOf _form_Click 'Splashが表示されるまでCloseSplashメソッドをブロックする AddHandler _form.Activated, AddressOf _form_Activated 'Splashフォームを表示する Application.Run(_form) End Sub 'SplashのCloseメソッドを呼び出す Private Shared Sub CloseSplashForm() If Not _form.IsDisposed Then _form.Close() End If End Sub 'メインフォームのActivateメソッドを呼び出す Private Shared Sub ActivateMainForm() If Not _mainForm.IsDisposed Then _mainForm.Activate() End If End Sub 'Splashフォームがクリックされた時 Private Shared Sub _form_Click(ByVal sender As Object, _ ByVal e As EventArgs) 'Splashフォームを閉じる CloseSplash() End Sub 'メインフォームがアクティブになった時 Private Shared Sub _mainForm_Activated(ByVal sender As Object, _ ByVal e As EventArgs) 'Splashフォームを閉じる CloseSplash() End Sub 'Splashフォームが表示された時 Private Shared Sub _form_Activated(ByVal sender As Object, _ ByVal e As EventArgs) RemoveHandler _form.Activated, AddressOf _form_Activated 'CloseSplashメソッドの待機を解除 If Not splashShownEvent Is Nothing Then splashShownEvent.Set() End If End Sub
//Splashフォーム private static SplashForm _form = null; //メインフォーム private static Form _mainForm = null; //Splashを表示するスレッド private static System.Threading.Thread _thread = null; //lock用のオブジェクト private static readonly object syncObject = new object(); //Splashが表示されるまで待機するための待機ハンドル private static System.Threading.ManualResetEvent splashShownEvent = null; /// <summary> /// Splashフォーム /// </summary> public static SplashForm Form { get { return _form; } } /// <summary> /// Splashフォームを表示する /// </summary> /// <param name="mainForm">メインフォーム</param> public static void ShowSplash(Form mainForm) { lock (syncObject) { if (_form != null || _thread != null) { return; } _mainForm = mainForm; //メインフォームのActivatedイベントでSplashフォームを消す if (_mainForm != null) { _mainForm.Activated += new EventHandler(_mainForm_Activated); } //待機ハンドルの作成 splashShownEvent = new System.Threading.ManualResetEvent(false); //スレッドの作成 _thread = new System.Threading.Thread( new System.Threading.ThreadStart(StartThread)); _thread.Name = "SplashForm"; _thread.IsBackground = true; _thread.SetApartmentState(System.Threading.ApartmentState.STA); //.NET Framework 1.1以前では、以下のようにする //_thread.ApartmentState = System.Threading.ApartmentState.STA; //スレッドの開始 _thread.Start(); } } /// <summary> /// Splashフォームを表示する /// </summary> public static void ShowSplash() { ShowSplash(null); } /// <summary> /// Splashフォームを消す /// </summary> public static void CloseSplash() { lock (syncObject) { if (_thread == null) { return; } if (_mainForm != null) { _mainForm.Activated -= new EventHandler(_mainForm_Activated); } //Splashが表示されるまで待機する if (splashShownEvent != null) { splashShownEvent.WaitOne(); splashShownEvent.Close(); splashShownEvent = null; } //Splashフォームを閉じる //Invokeが必要か調べる if (_form != null) { if (_form.InvokeRequired) { _form.Invoke(new MethodInvoker(CloseSplashForm)); } else { CloseSplashForm(); } } //メインフォームをアクティブにする if (_mainForm != null) { if (_mainForm.InvokeRequired) { _mainForm.Invoke(new MethodInvoker(ActivateMainForm)); } else { ActivateMainForm(); } } _form = null; _thread = null; _mainForm = null; } } //スレッドで開始するメソッド private static void StartThread() { //Splashフォームを作成 _form = new SplashForm(); //Splashフォームをクリックして閉じられるようにする _form.Click += new EventHandler(_form_Click); //Splashが表示されるまでCloseSplashメソッドをブロックする _form.Activated += new EventHandler(_form_Activated); //Splashフォームを表示する Application.Run(_form); } //SplashのCloseメソッドを呼び出す private static void CloseSplashForm() { if (!_form.IsDisposed) { _form.Close(); } } //メインフォームのActivateメソッドを呼び出す private static void ActivateMainForm() { if (!_mainForm.IsDisposed) { _mainForm.Activate(); } } //Splashフォームがクリックされた時 private static void _form_Click(object sender, EventArgs e) { //Splashフォームを閉じる CloseSplash(); } //メインフォームがアクティブになった時 private static void _mainForm_Activated(object sender, EventArgs e) { //Splashフォームを閉じる CloseSplash(); } //Splashフォームが表示された時 private static void _form_Activated(object sender, EventArgs e) { _form.Activated -= new EventHandler(_form_Activated); //CloseSplashメソッドの待機を解除 if (splashShownEvent != null) { splashShownEvent.Set(); } }
前のようにApplication.Idleイベントでスプラッシュウィンドウを閉じると、スプラッシュウィンドウは表示されたと思うと、あっという間に閉じてしまいますので、別のタイミングで閉じるようにします。ここでは、メインフォームのActivatedイベントで閉じています。(さらにSplashFormのClickイベントでもSplashFormを閉じるようにしています。)
SplashFormを表示するには、前と同様、SplashForm.ShowSplashメソッドを呼び出しますが、このときメインフォームのインスタンスを指定できます。メインフォームを指定すると、そのActivatedイベントハンドラで自動的にSplashFormが閉じられます。メインフォームを指定しなかった時は、適当な位置(メインウィンドウのActivatedイベントハンドラなど)でSplashForm.CloseSplashメソッドを呼び出してSplashFormを閉じてください。
上のコードを使ってスプラッシュウィンドウを表示させるコードを以下に示します。ここでも、エントリポイントであるMainメソッドでメインフォームForm1を表示させる前にスプラッシュウィンドウを表示させています。
<STAThread()> _ Shared Sub Main() Dim mainForm As New Form1 'スプラッシュウィンドウを表示 SplashForm.ShowSplash(mainForm) 'メインウィンドウを表示 Application.Run(mainForm) End Sub
[STAThread] static void Main() { Form1 mainForm = new Form1(); //スプラッシュウィンドウを表示 SplashForm.ShowSplash(mainForm); //メインウィンドウを表示 Application.Run(mainForm); }
補足:スプラッシュウィンドウは、アプリケーションが起動してから準備ができるまでに長い時間がかかる時に、ユーザーを不安にさせたり、待たせたりしないために表示するというのが主要な使用目的でしょう。そのため、起動にそれほど時間のかからないアプリではスプラッシュウィンドウは必要ありません。ユーザーの立場からすると、無駄なスプラッシュウィンドウにはうんざりさせられます。