はじめに
明日の自分のためにメモっとく。
ポイントは、別スレッドで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;
}