DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

スプラッシュウィンドウを表示する

「スプラッシュウィンドウ(Splash Window)」とは、アプリケーション起動時に真っ先に表示され、アプリケーションのロゴやバージョンなどの情報を表示し、自動的に消える(消えないものも稀にありますが)ウィンドウです。Microsoft Visual Studioや、Officeなど、多くのアプリケーションでスプラッシュウィンドウが使われています。

なお、Visual Studio 2005以降のVB.NETをお使いであれば、もっと簡単な方法があります。この方法に関しては、「VB.NETでスプラッシュウィンドウを表示する」をご覧ください。

スプラッシュウィンドウに必要な機能

まず、スプラッシュウィンドウに絶対必要な機能をあげてみます。

  1. メインウィンドウより先に表示される。
  2. 画面中央に表示される。
  3. ウィンドウにタイトルバーや枠が無い。
  4. アプリケーションの準備ができたところで、自動的に閉じる。

1.に関しては、エントリポイント(Mainメソッド)または、メインフォームのコンストラクタやLoadイベントハンドラなどでメインフォームが表示される前にスプラッシュウィンドウを表示するようにすればよいでしょう。2.に関してはスプラッシュウィンドウのフォームのStartPositionプロパティをCenterScreenに、3.に関してはFormBorderStyleプロパティをNoneにすればよいでしょう。

問題は4.です。どのタイミングでスプラッシュウィンドウを閉じるべきでしょうか?メインフォームが表示されるタイミングでということであれば、メインフォームのActivatedイベントで閉じるようにすればよいでしょう。また、「アプリケーションの準備ができた」ということを「アプリケーションがアイドル状態になった時」と解釈すると、Application.Idleイベントで閉じる方法も考えられます。さらに、メインフォームのLoadイベントハンドラの最後で閉じる方法もあります。起動してから指定時間までスプラッシュウィンドウを表示したいときは、タイマーを使う方法もあるかもしれません(個人的な意見としては、スプラッシュウィンドウは用が無くなったらさっさと閉じるべきだと思いますので、この方法はお勧めしたくありません)。

以上の機能以外に、場合によっては、次のような機能も必要になるでしょう。

  1. タスクバーに表示しない。(スプラッシュウィンドウがタスクバーに表示されるアプリも多いです。)
  2. メインウィンドウより手前に表示される。

5.については、フォームのShowInTaskbarプロパティをFalseにすればよいでしょう。6.については、フォームのTopMostプロパティをTrueにする方法があります。しかし、個人的な意見としては、TopMostにはせずに、メインウィンドウの後ろに隠れないようにAddOwnedFormメソッド等を使用するにとどめた方がよいと思います。

スプラッシュウィンドウ作成の実際

以上のことを踏まえ、実際にスプラッシュウィンドウを作ってみます。

まずプロジェクトに新しいフォームを追加し、名前を「SplashForm」とします。SplashFormのStartPositionプロパティをCenterScreen、FormBorderStyleプロパティをNone、さらにShowInTaskbarプロパティをFalseにします。その他、画像の配置など、SplashFormのデザインを適当に行ってください。

さらにSplashFormクラスに次のようなコードを記述します。ここではApplication.IdleイベントでSplashFormを閉じることにします。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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を表示させる前にスプラッシュウィンドウを表示させています。エントリポイントの意味が分からなければ、まずこちらをご覧ください。

VB.NET
コードを隠すコードを選択
<STAThread()> _
Shared Sub Main()
    'スプラッシュウィンドウを表示
    SplashForm.ShowSplash()

    'メインウィンドウを表示
    Application.Run(New Form1)
End Sub
C#
コードを隠すコードを選択
[STAThread]
static void Main() 
{
    //スプラッシュウィンドウを表示
    SplashForm.ShowSplash();
    
    //メインウィンドウを表示
    Application.Run(new Form1());
}

スプラッシュウィンドウを別スレッドで表示する

次にスプラッシュウィンドウをメインスレッドとは別のスレッドで表示させるようにしてみます。

まず、スプラッシュウィンドウのフォームクラスは先の例と同様の方法で作成しておき(ここでもクラスの名前を"SplashForm"とします)、SplashFormクラス内に次のようなコードを記述してください。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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を表示させる前にスプラッシュウィンドウを表示させています。

VB.NET
コードを隠すコードを選択
<STAThread()> _
Shared Sub Main()
    Dim mainForm As New Form1

    'スプラッシュウィンドウを表示
    SplashForm.ShowSplash(mainForm)

    'メインウィンドウを表示
    Application.Run(mainForm)
End Sub
C#
コードを隠すコードを選択
[STAThread]
static void Main() 
{
    Form1 mainForm = new Form1();

    //スプラッシュウィンドウを表示
    SplashForm.ShowSplash(mainForm);
    
    //メインウィンドウを表示
    Application.Run(mainForm);
}
補足:スプラッシュウィンドウは、アプリケーションが起動してから準備ができるまでに長い時間がかかる時に、ユーザーを不安にさせたり、待たせたりしないために表示するというのが主要な使用目的でしょう。そのため、起動にそれほど時間のかからないアプリではスプラッシュウィンドウは必要ありません。ユーザーの立場からすると、無駄なスプラッシュウィンドウにはうんざりさせられます。
  • 履歴:
  • 2005/9/23 マルチスレッドによるサンプルを修正しました。
  • 2012/4/10 「スプラッシュウィンドウを別スレッドで表示する」のコードを書きなおしました。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。