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

別スレッドでスプラッシュウィンドウを表示するコードを紹介します。

概要

スプラッシュウィンドウの表示方法をこちらの記事で紹介しましたが、この方法ではメインスレッドで時間のかかる処理をするとスプラッシュウィンドウがフリーズします。そのため定期的にメインスレッドからスプラッシュフォームのupdate()メソッドを呼び出す必要があります。スプラッシュウィンドウが固まらない方法として、別スレッドでスプラッシュウィンドウを表示する方法があります。
この記事ではスプラッシュウィンドウを別スレッドで表示するコードを紹介します。

プログラム

UI

以下のUIを準備します。

メインフォーム

新規作成したフォームをそのまま利用します。変更はありません。

スプラッシュフォーム

FormBorderStyleプロパティを"None"に、StartPositionプロパティを"CenterScreen"に変更します。

コード

下記のコードを記述します。
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace MultiThreadSplashWindow
{
  static class Program
  {
    static FormSplash fs;
    static object lockobj;

    /// <summary>
    /// アプリケーションのメイン エントリ ポイントです。
    /// </summary>
    [STAThread]
    static void Main()
    {
      lockobj = new object();

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);

      lock (lockobj) {
        System.Threading.ThreadStart ts = new System.Threading.ThreadStart(StartThread);
        static System.Threading.Thread thread = new System.Threading.Thread(ts);
        thread.Name = "Splash";
        thread.IsBackground = true;
        thread.SetApartmentState(System.Threading.ApartmentState.STA);
        thread.Start();
      }
      //時間のかかる処理
      System.Threading.Thread.Sleep(2000);

      CloseSplash();

      Application.Run(new FormMain());
    }

    private static void StartThread()
    {
      fs = new FormSplash();
      Application.Run(fs);
    }

    private delegate void dop();
    private static void CloseSplash()
    {
      dop d = new dop(fs.Close);
      fs.Invoke(d);
    }
  }
}

解説

下記のコードでスプラッシュウィンドウを表示するスレッドを作成します。Threadのコンストラクタではスレッドの開始メソッドを指定します。
//スレッド作成
System.Threading.ThreadStart ts = new System.Threading.ThreadStart(StartThread);
System.Threading.Thread thread = new System.Threading.Thread(ts);
thread.Name = "Splash";
thread.IsBackground = true;
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();

今回の例では、メインスレッド側で時間のかかる処理を実行します。時間のかかる処理が終了したのちスプラッシュウィンドウを閉じ、メインフォームを指定してアプリケーションの実行を開始します。
//時間のかかる処理
System.Threading.Thread.Sleep(2000);

CloseSplash();

Application.Run(new FormMain());

スレッドの開始メソッドと、スプラッシュウィンドウを閉じるコードです。スプラッシュウィンドウは別のスレッドで表示されるためInvokeメソッドを呼び出して閉じる必要があります。
private static void StartThread()
{
  fs = new FormSplash();
  Application.Run(fs);
}

private delegate void dop();
private static void CloseSplash()
{
  dop d = new dop(fs.Close);
  fs.Invoke(d);
}

実行結果

アプリケーションを起動するとスプラッシュフォームが表示されます。


2秒ほど経過すると、メインフォームが表示されます。

補足 : デリゲートを別のメソッドに分ける

デリゲートを別のメソッドに分けたコードです。一般的にはこちらのコードのほうが可読性が高いです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace MultiThreadSplashWindow
{
  static class Program
  {
    static FormSplash fs;
    static object lockobj;

    /// <summary>
    /// アプリケーションのメイン エントリ ポイントです。
    /// </summary>
    [STAThread]
    static void Main()
    {
      lockobj = new object();

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);

      lock (lockobj) {
        System.Threading.ThreadStart ts = new System.Threading.ThreadStart(StartThread);
        System.Threading.Thread thread = new System.Threading.Thread(ts);
        thread.Name = "Splash";
        thread.IsBackground = true;
        thread.SetApartmentState(System.Threading.ApartmentState.STA);
        thread.Start();
      }
      //時間のかかる処理
      System.Threading.Thread.Sleep(2000);

      CloseSplash();

      Application.Run(new FormMain());
    }

    private static void StartThread()
    {
      fs = new FormSplash();
      Application.Run(fs);
    }


    private static void CloseSplash()
    {
      dop d = new dop(CloseForm);
      fs.Invoke(d);
    }

    private delegate void dop();
    private static void CloseForm()
    {
      fs.Close();
    }
  }
}

補足 : 進行状況を表示する

スプラッシュフォーム側にラベルを一つ追加し、メッセージを表示するデリゲートを作成した例が以下になります。処理の進行状況がスプラッシュフォームに表示されます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace MultiThreadSplashWindowProgress
{
  static class Program
  {
    static FormSplash fs;
    static object lockobj;

    /// <summary>
    /// アプリケーションのメイン エントリ ポイントです。
    /// </summary>
    [STAThread]
    static void Main()
    {
      lockobj = new object();

      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);

      lock (lockobj) {
        System.Threading.ThreadStart ts = new System.Threading.ThreadStart(StartThread);
        System.Threading.Thread thread = new System.Threading.Thread(ts);
        thread.Name = "Splash";
        thread.IsBackground = true;
        thread.SetApartmentState(System.Threading.ApartmentState.STA);
        thread.Start();

        //時間のかかる処理
        dmes d = new dmes(ShowMessage);
        if (fs != null) {
          fs.Invoke(d, "初期化しています");
        }
        System.Threading.Thread.Sleep(500);
        if (fs != null) {
          fs.Invoke(d, "設定ファイルの読み込み中");
        }
        System.Threading.Thread.Sleep(500);
        if (fs != null) {
          fs.Invoke(d, "ローカルデータベースの初期化中");
        }
        System.Threading.Thread.Sleep(500);
        if (fs != null) {
          fs.Invoke(d, "アカウント情報読み込み中");
        }
        System.Threading.Thread.Sleep(500);
        if (fs != null) {
          fs.Invoke(d, "画面の構成をしています。");
        }
        System.Threading.Thread.Sleep(500);
        if (fs != null) {
          fs.Invoke(d, "準備完了");
        }
      }

      CloseSplash();

      Application.Run(new FormMain());

    }
    
    private static void StartThread()
    {
      fs = new FormSplash();
      Application.Run(fs);
    }


    private static void CloseSplash()
    {
      dop d = new dop(CloseForm);
      if (fs != null) {
        fs.Invoke(d);
      }
    }

    private delegate void dop();
    private static void CloseForm()
    {
      fs.Close();
    }

    private delegate void dmes(string message);
    private static void ShowMessage(string message)
    {
      fs.label1.Text = message;
    }

  }
}
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2011-12-01
iPentec all rights reserverd.