2012-02-29 9 views
3

私はいくつかの機器でGPIBコマンドを使用していくつかの機器と通信するアプリケーションを作成中です。私は新しいスレッドを開始し、テストを実行するクラスTestProcedureをセットアップしました。テスト中、私はいくつかのカスタムイベントを設定して、情報をGUIに送り返しました。このようにテキストボックスを更新し、GUIを介して処理されるだろうC#:Events&Thread Safe GUIアップデート

public event InformationEventHandler<string> TestInfoEvent; 
    /// <summary> 
    /// Event raised when information should be passed back to the main testing form. 
    /// </summary> 
    /// <param name="s">Information to send back to form.</param> 
    private void OnInfo(string s) 
    { 
     if (TestInfoEvent != null) 
      TestInfoEvent(this, s); 
    } 

TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string> 
           (InfoOccurred); 
.... 
private void InfoOccurred(Object sender, string s) 
{ 
    this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text; 
    if (this.textBox1.Text.Length > 10000) 
     this.textBox1.Text = this.textBox1.Text.Remove(1000); 
} 

このイベントの処理が正常に動作しているようだここでは簡単なイベントの例があります。私はクロススレッドの問題を受けていない、と全体的に期待どおりに動作している。しかし、別のフォームでは、同様のイベントハンドラを追加しました。これは、クロススレッド例外をスローします。イベントは発生し、InputTextBox(カスタムComponentOneコントロール)に表示される情報のビットを持つ単純なクラスを送信します。特定のコントロールには.Invokeメソッドがないため、非同期にアクセスするための代替ソリューションを探しています。

私の質問は、イベントハンドラがフォーム上のコントロールにアクセスすることは安全ですか?そうでなければ、イベントハンドラはどのように起動し、誰かが私の教育を助けたり、リンクされた情報を提供したりすることができますか?イベントをロックする必要はありますか?

答えて

3

UIスレッドのコントロールは、UIスレッドからのみアクセスできます。他のスレッドからのアクセスは、問題の原因となります。 InvokeRequiredBeginInvoke()を使用して、イベントが正しいスレッドにマーシャリングされていない場合は、マーシャリングします。

Example

+0

私はイベント自体への呼び出しを追加する必要がありますか?同じことが、テキストボックスの呼び出しと同じように機能しますか? 編集:申し訳ありません、あなたのサンプルを見ましたよね。 – Corey

+0

イベントハンドラ関数の内部で 'InvokeRequired'を最初にチェックし、' true'を返す場合は、UIスレッドへの呼び出しをマーシャリングする必要があります。 BeginInvoke() '戻り値が 'false'の場合は、既にUIスレッド上にあるので、直接コントロールに直接アクセスすることができます(例えば、' txtName.Text = ...; 'は有効です)。失敗するかもしれません)。 – xxbbcc

+1

'BeginInvoke()'を参照する答えは、 'Control.Invoke()'ではなく、必要である可能性があるので修正しました。 – xxbbcc

1

あなたは、デリゲートのコールバックを作成し、Invoke()InvokeRequiredプロパティをテストした後のことを実行することをお勧めします。次のコードは、スレッドセーフな方法で追加を処理します。

TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string> 
          (InfoOccurred); 

private void InfoOccurred(Object sender, string s) 
{ 
    LogMessage(s); 
} 

delegate void LogMessageCallback(string text); 

void LogMessage(String message) 
{ 
    if (this.textBox1.InvokeRequired) 
     this.Invoke(new LogMessageCallback(LogMessage), message); 
    else 
    { 
     this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text; 
     if (this.textBox1.Text.Length > 10000) 
      this.textBox1.Text = this.textBox1.Text.Remove(1000); 
    } 
} 
+0

「ログ」とは何ですか?これは 'this.InvokeRequired'でなければなりません。 –

+0

申し訳ありませんが、スニペットを修正しました。 – Walk