LoginSignup
13
16

More than 5 years have passed since last update.

【備忘録】別スレッドからメインスレッドのフォームを操作する

Last updated at Posted at 2015-03-07

はじめに

明日の自分のためにメモっとく。

ポイントは、別スレッドでSystem.Windows.Forms.Controlを受け取り、Invokeを使ってイベントを通知すること。

環境

  • Windows7 Professional SP1
  • Microsoft Visual Studio Version 12.0.31101.00 Update 4
  • Microsoft .NET Framework Version 4.5.51209
  • Microsoft Visual C# 2013
  • NLog 3.2.0.0

Example

Form1.cs
    /// <summary>
    /// 【備忘録】別スレッドからメインスレッドのフォームを操作する
    /// </summary>
    public partial class Form1 : Form
    {
        private NLog.Logger logger = NLog.LogManager.GetLogger("fooLogger");
        private bool isSubThreadAllFinished = false;
        private int runSubThreads;
        private int endSubThreads;

        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 別スレッドからフォームのテキストボックスへ値を入れる
        /// </summary>
        /// <param name="sender">呼び元オブジェクト</param>
        /// <param name="e">イベントデータ</param>
        private async void button1_Click(object sender, EventArgs e)
        {
            WorkerClass wc = null;
            int minThreads = 10;

            try
            {
                isSubThreadAllFinished = false;
                runSubThreads = 0;

                wc = new WorkerClass(logger, this);
                wc.OnSubThreadRunning += Form1_SubThreadRunning;
                wc.OnSubThreadFinished += Form1_SubThreadFinished;

                // スレッドプールスレッドが実行するメソッドを設定
                WaitCallback waitCallback = new WaitCallback(wc.Execute);

                // スレッドプールスレッドの最小数を変更
                ThreadPool.SetMinThreads(minThreads, minThreads);

                // 別スレッドを作る
                for (int i = 0; i < minThreads; i++)
                {
                    // メソッドをスレッドプールキューに登録
                    ThreadPool.QueueUserWorkItem(waitCallback, 1000 * i);
                    runSubThreads++;
                }

                // 別メソッドの終了数が実行した別メソッドの総数になったらループを抜ける
                await Task.Run(() => { 
                    while (!isSubThreadAllFinished)
                    {
                        logger.Info("終了まで待機中...");
                    }
                });
            }
            catch (Exception ex)
            {
                logger.Error(ex);
            }
            finally
            {
                this.textBox1.Text = "別スレッドは全て終了";

                if (wc != null)
                {
                    wc = null;
                }
            }

        }

        /// <summary>
        /// 別スレッドが実行された時に呼ばれるイベントメソッド
        /// </summary>
        /// <param name="sender">呼び元オブジェクト</param>
        /// <param name="e">ユーザ定義イベントデータ</param>
        void Form1_SubThreadRunning(object sender, FooEventArgs e)
        {
            this.textBox1.Text = e.Message;
        }

        /// <summary>
        /// 別スレッドが終了するたびに呼ばれるイベントメソッド
        /// </summary>
        /// <param name="sender">呼び元オブジェクト</param>
        /// <param name="e">ユーザ定義イベントデータ</param>
        private void Form1_SubThreadFinished(object sender, EventArgs e)
        {
            // 終了した別メソッドをカウント
            endSubThreads++;
            // 別メソッドの終了数が実行した別メソッドの総数になったか判定
            isSubThreadAllFinished = (runSubThreads <= endSubThreads);
        }
    }
WorkerClass.cs
    /// <summary>
    /// 別スレッドで実行するクラス
    /// </summary>
    class WorkerClass
    {
        private NLog.Logger logger;
        private Control mainThreadForm;

        /// <summary>
        /// 別スレッドの実行通知用デリゲート(イベントハンドラ)
        /// </summary>
        /// <param name="sender">呼び元オブジェクト</param>
        /// <param name="e">ユーザ定義イベントデータ</param>
        public delegate void SubThreadRunningEventHandler(object sender, FooEventArgs e);
        /// <summary>
        /// 別スレッドの実行通知用イベント
        /// </summary>
        public event SubThreadRunningEventHandler OnSubThreadRunning;
        /// <summary>
        /// 別スレッドの終了通知用イベント
        /// </summary>
        public event EventHandler OnSubThreadFinished;

        /// <summary>
        /// カスタムコンストラクタ
        /// </summary>
        /// <param name="aLogger">NLog</param>
        /// <param name="aMainThreadForm">メインスレッドのフォーム</param>
        public WorkerClass(NLog.Logger aLogger, Control aMainThreadForm)
        {
            logger = aLogger;
            mainThreadForm = aMainThreadForm;
        }

        /// <summary>
        /// 別スレッドの実行メソッド
        /// </summary>
        /// <param name="state">パラメータ</param>
        public void Execute(object state)
        {
            try
            {
                // 渡されたパラメータの値だけ待機する
                Thread.Sleep((int)state);

                // Invoke()を使って実行を通知する
                FooEventArgs e = new FooEventArgs();
                e.Message = state.ToString();
                mainThreadForm.Invoke(OnSubThreadRunning, new object[] { this, e });
            }
            finally
            {
                // Invoke()を使って終了を通知する
                mainThreadForm.Invoke(OnSubThreadFinished, new object[] { this, EventArgs.Empty });
            }
        }
    }
FooEventArgs.cs
    /// <summary>
    /// ユーザ定義イベントデータクラス
    /// </summary>
    class FooEventArgs : EventArgs
    {
        /// <summary>文字列をもたせる</summary>
        public string Message;
    }
13
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
16