- PR -

(C#2.0)別スレッドからフォームを制御

投稿者投稿内容
紫苑
会議室デビュー日: 2007/02/15
投稿数: 17
投稿日時: 2007-03-14 10:53
お世話になっております。

複数のスレッドから一つのラベルコントロールを制御(Textの変更)したいと
思っているのですが、実行した際に"有効でないスレッド間の操作"というエラー
が発生します。2005からコントロールはスレッドセーフで無くなったらしいの
でInvokeなどを使用して回避しようと思っているのですが、なかなかうまくいきません・・・

具体的には
スレッド内でイベントを発生させ、独自のEventArgsに文字列をセットし、
ラベルに文字列をセットする。
と、言う事を行いたいのですが、色々調べてみたところ、イベントを使わなかった場合しか見つけられませんでした。Eventや、Invokeなどの機能をもっと理解していれば
わかるとは思うのですが・・・

ソースは以下の通りです、大体の処理の流れの部分だけ抜粋してみました。

private delegate void SetLabelEventHandler( SetLabelEventArgs e);
private event SetLabelEventHandler SetLabel;

private class SetLabelEventArgs : EventArgs
{
private string _SetString;

public string SetString
{
get{return this._SetString;}
}

public SetLabelEventArgs(string sval)
{
this._SetString = sval;
}
}

private void スレッドメソッド()
{
this.SetLabel(new SetLabelEventArgs("AAAAAA");
}

private void Form1_Load(object sender, System.EventArgs e)
{
System.Threading.Thread thread;

SetLabel += new SetLabelEventHandler(OnSetLabel);
thread = new System.Threading.Thread(new ThreadStart(スレッドメソッド));
thread.Start();

}

private void OnSetLabel(SetLabelEventArgs e)
{
label1.Text = e.SetString;
}

ご教授の程、宜しくお願い致します。


[ メッセージ編集済み 編集者: 紫苑 編集日時 2007-03-14 11:56 ]

[ メッセージ編集済み 編集者: 紫苑 編集日時 2007-03-16 08:52 ]
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-03-14 11:04
「うまくいかない」だけだと、回答しようと思った人が
ソースを全部読んで怪しいところがないかを確認する必要がありますよね。
質問するときには「どのようにうまくいかないのか」を書くようにしてください。
(どの行でどんな例外が発生する とか コンパイルエラーになる とか)

引用:

2005からコントロールはスレッドセーフで無くなったらしいので


.NET2.0になる前からコントロールはスレッドセーフではなかったはずです。
今までは例外として検出されなかっただけです。
紫苑
会議室デビュー日: 2007/02/15
投稿数: 17
投稿日時: 2007-03-14 11:26
回答ありがとうございます。

確かにアバウト過ぎですね、
実際に例外が発生したり、コンパイルエラーになる以前に
記載したソースのどこに手をつけていいのかすらわかっていません・・・
質問する以前の段階のようなので、もう少し勉強してから具体的な質問
をすることにします。
よこけん
大ベテラン
会議室デビュー日: 2006/01/31
投稿数: 216
投稿日時: 2007-03-14 11:29
提示されたコードでイベントを発生させているのは別スレッドですので
その結果実行されるイベントハンドラも、その別スレッドが実行します。
つまり、イベントを利用した所で何の解決にもなりません。
Control.Invokeメソッドを利用してください。 (イベントを使うかどうかは別問題です。)

こちらの記事でControl.Invokeにメソッドついて解説しています。 ( 自分のブログですが^^; )
参考になれば幸いです。
C#と諸々 Windowsアプリケーションにおけるマルチスレッドの注意点

# 「イベントを発生させているのは別スレッドです。」から「提示されたコードでイベントを発生させているのは別スレッドですので」に修正

[ メッセージ編集済み 編集者: よこけん 編集日時 2007-03-14 11:46 ]
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2007-03-14 11:36
this.SetLabelという呼び出し箇所を
ControlクラスのInvokeメソッド経由に書き換えればよいと思います。

余談ですが、スレッドを起動するクラスと画面のクラスとを別々にする場合は、
ProcessクラスのSynchronizingObjectプロパティのように実装するのがよいでしょう。
(SynchronizingObjectにはコントロールをセットして使います。
Processクラス内部ではSynchronizingObjectに設定されたオブジェクトの
Invoke系メソッド経由でスレッドからのイベントを起動します)
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2007-03-14 11:41
どうしてもイベント処理の中でやりたいならこうかな。
コード:

private void OnSetLabel(SetLabelEventArgs e)
{
Invoke(new SetLabelEventHandler(OnSetLabel2), e);
}

private void OnSetLabel2(SetLabelEventArgs e)
{
label1.Text = e.SetString;
}



Invokeもデリゲートを使うし、イベントもデリゲート型という共通事項があるだけで、Invokeのためにイベントを使うというのはイベントの使い方を間違っていると思います。

[ メッセージ編集済み 編集者: 一郎 編集日時 2007-03-14 11:41 ]
紫苑
会議室デビュー日: 2007/02/15
投稿数: 17
投稿日時: 2007-03-14 12:18
みなさん回答ありがとうございます。

イベントは解決方法のために使用したのではなく、別スレッドからの
処理状況通知のために使用しています。

よねKENさんがおっしゃった、『ProcessクラスのSynchronizingObjectプロパティのように実装』を試してみようと思います。

まずは勉強しないとですが・・・^^;

ありがとうございました。
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-03-14 12:22
引用:

実行した際に"有効でないスレッド間の操作"というエラーが発生します



すみません。ちゃんと事象書いてありましたね。
見落としてました。失礼しました。

スキルアップ/キャリアアップ(JOB@IT)