Windows.Forms でスレッドを使う方法

投稿日 2015/09/06 Visual Studio 2015 対応

[Home] | [C# の記事]

概要

Windows.Forms でスレッドを使うとき、スレッド側からフォームのコントロールを直接、使用できないという制限がある。このような場合は Invoke メソッドを使って間接的にフォーム上のコントロールにアクセスする。


サンプルの説明

次のサンプルは、スレッドを使ってテキストボックスに入力された数をカウントダウンして表示する。


コード

Form1.cs

このコードでの勘所は、Invoke を使ってコントロールの操作を行う場所である。これは、action() で行っている。Invoke は引数にデリゲートオブジェクトを取る。このデリゲートは delegate void DelegateCall() と定義している。

using System;
using System.Windows.Forms;
using System.Diagnostics;

namespace ThreadTest
{
    public partial class Form1 : Form
    {
        // 独自のタイマーオブジェクト
        private TimerClass timer;
        
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Form1()
        {
            InitializeComponent();
            
            // タイマーオブジェクトをインスタンス化
            timer = new TimerClass();
            // イベントを設定
            timer.tick += action;
        }
        
        /// <summary>
        /// 開始ボタンがクリックされたとき
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                long count = Int32.Parse(textBox1.Text);
                timer.Start(count);
                label1.Text = "Started";
            }
            catch (Exception ex)
            {
                label1.Text = ex.Message;
                Debug.WriteLine(ex.Message);
            }
        }
        
        /// <summary>
        /// 停止ボタンがクリックされたとき
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            timer.Stop();
            label1.Text = "Stopped";
            timer = new TimerClass();
            timer.tick += action;
            textBox1.Text = "10";
        }
        
        /// <summary>
        /// Invoke 用デリゲート
        /// </summary>
        delegate void DelegateCall();
        
        /// <summary>
        /// Tick イベントハンドラ
        /// </summary>
        private void action()
        {
            // コントロールはスレッドから直接操作できないので Invoke を使って操作する。
            Invoke(new DelegateCall(UpdateCount));
        }
        
        /// <summary>
        /// Invoke でコールするメソッド
        /// </summary>
        void UpdateCount()
        {
            try
            {
                // textBox1 の内容をカウントダウンして表示する。
                int n = Int32.Parse(textBox1.Text);
                n--;
                textBox1.Text = n.ToString();
                label1.Text = n == 0 ? "Stopped" : "Working";
                if (n == 0)
                {
                    btnStop_Click(null, null);
                }
            }
            catch
            {
            
            }
        }
    }
}

TimerClass.cs

このクラスはタイマー相当の機能をスレッドで実現している。タイマーイベントは、internal event Action tick; と定義している。Action は .NET 標準のデリゲートであり、Form1 側では UpdateCount() になっている。

using System;
using System.Threading;

namespace ThreadTest
{
    internal class TimerClass
    {
        internal event Action tick;  // タイマーイベント
        Thread thread1;
        long Count { get; set; } = 0;
        
        /// <summary>
        /// コンストラクタ
        /// </summary>
        internal TimerClass()
        {
            thread1 = new Thread(DoWork);
        }
        
        /// <summary>
        /// カウントダウン開始
        /// </summary>
        /// <param name="count"></param>
        internal void Start(long count)
        {
            this.Count = count;
            thread1.Start();
        }
        
        /// <summary>
        /// カウントダウン停止
        /// </summary>
        internal void Stop()
        {
            Count = 0;
        }
        
        /// <summary>
        /// ワーカーメソッド
        /// </summary>
        private void DoWork()
        {
            // Count が正の間、カウントダウンし500msごとにイベントを発生させる。
            while (Count > 0)
            {
                Count--;
                tick();
                Thread.Sleep(500);
            }
        }
    }
}

 

 


 

ページの先頭へ