|
分類:[C#]
VisualStudio2019で、C#の学習をしております。
当方、C++やJAVA等の他言語は多少の経験がありますが、C#は未経験です。
以下のようなテストアプリを作って、Taskの学習をしています。
・アプリ起動すると、ボタンが1つだけのForm1が起動する。
・Form1のボタンを押すと、Form2が起動する(Form1はForm2を起動する為だけのもの)。
・Form2は
・終了ボタン(押すとForm2を終了し、処理をForm1に戻す)「button1」
・ログ出力テキストボックス「textBox1」
だけがある。
・Form2起動時(Shownイベント)に、「処理タスク」を起動する。
・「処理タスク」は、ログ出力テキストボックスに対して、1秒間隔で"処理"という文字列を出力する。
■困っていること
Form2を起動し、1秒間隔で"処理"と出力されている途中に、終了ボタンを押すと
-----
System.InvalidOperationException: 'ウィンドウ ハンドルが作成される前、コントロールで Invoke または BeginInvoke を呼び出せません。'
-----
が発生する。
原因は、ログ出力テキストボックスがあるForm2が既に閉じられている為と思われる。
■Form2のコード(Form1は割愛)
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
/// <summary>
/// 「終了」ボタン押下
/// </summary>
private void button1_Click(object sender, EventArgs e)
{
// フォームを閉じて上位に返す
this.DialogResult = DialogResult.OK;
this.Close();
}
/// <summary>
/// フォーム表示直後に一度だけ実行されるShownイベントのコールバック
/// </summary>
private void Form2_Shown(object sender, EventArgs e)
{
// テキストボックスにログ出力
// (このテキストボックスは、メインタスクからも処理タスクからも出力処理が行われる)
textBox1.AppendText("UDP通信タスクを起動します" + "\r\n");
// 「処理タスク」を起動
Task task = Task.Run(() => {
RunProcTask();
});
}
/// <summary>
/// 処理タスク
/// </summary>
private void RunProcTask()
{
// 1秒ごとにログを出力する
while (true) {
// 1秒待ってからログ出力
Thread.Sleep(1000);
//textBox1.AppendText("処理" + "\r\n"); //←このような出力はできない
Invoke(new Action<string>(this._PutLog), "処理"); //←ここで例外が発生する
}
}
/// <summary>
/// 処理タスクからログ出力を行う
/// </summary>
private void _PutLog(string msg)
{
textBox1.AppendText(msg + "\r\n");
}
}
■教えてほしいこと
・処理タスクからログを出力する方法として、色々調べた結果上記のようにInvokeを用いていますが、
効率や可読性・処理速度等、総合的に見てベストな対応でしょうか?
・InvalidOperationExceptionを回避する方法として
・Form2に「static bool flg;」というメンバを用意し、Form2_Shownメソッド冒頭でtrueセット、button1_Clickメソッド冒頭でfalseセット。
・「Invoke(new Action<string>(this._PutLog), "処理");」は、flgがtrueの場合のみ実行
という対応をしました。
「Form1起動→ボタンを押下しForm2起動→"処理"出力中に終了ボタン押下→無事Form1に戻る」まではいいのですが、
再度Form1のボタンを押下しForm2を起動したところ、
System.InvalidOperationException: 'ウィンドウ ハンドルが作成される前、コントロールで Invoke または BeginInvoke を呼び出せません。'
というエラーが発生しました。
何か足らないことがあるのでしょうか?
・上記の「Form2が存在するかどうかをフラグで判定」という方法はベストではない気がしています。
本当は、終了ボタン押下時に「処理タスクを止める」という事をしたかったのですが、やり方が分からず・・・。
ベストな実装はどのようなものでしょうか?
長くなりましたが、よろしくお願い致します。
|