210

UART を使用してマイクロコントローラーから温度値を C# インターフェイスに送信し、温度を表示したいと考えていますLabel.Content。ここに私のマイクロコントローラコードがあります:

while(1) {
   key_scan(); // get value of temp
   if (Usart_Data_Ready())
   {
      while(temperature[i]!=0)
      {
         if(temperature[i]!=' ')
         {
            Usart_Write(temperature[i]);
            Delay_ms(1000);
         }
         i = i + 1;
      }
      i =0;
      Delay_ms(2000);
   }
}

私のC#コードは次のとおりです。

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
   txt += serialPort1.ReadExisting().ToString();
   textBox1.Text = txt.ToString();
}

しかし、そこで例外が発生します "クロススレッド操作が無効です: コントロール 'textBox1' は、それが作成されたスレッド以外のスレッドからアクセスされました" マイクロコントローラーから温度文字列を取得し、このエラーを削除する方法を教えてください!

4

6 に答える 6

348

メソッドで受信したデータはserialPort1_DataReceived、UI スレッド以外の別のスレッド コンテキストから取得されているため、このエラーが表示されます。
これを解決するには、MSDN の記事で説明されているように、ディスパッチャーを使用する必要があります:
方法: Windows フォーム コントロールへのスレッド セーフな呼び出しを行う

したがって、メソッドでテキスト プロパティを直接設定する代わりに、次のserialport1_DataReceivedパターンを使用します。

delegate void SetTextCallback(string text);

private void SetText(string text)
{
  // InvokeRequired required compares the thread ID of the
  // calling thread to the thread ID of the creating thread.
  // If these threads are different, it returns true.
  if (this.textBox1.InvokeRequired)
  { 
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
  }
  else
  {
    this.textBox1.Text = text;
  }
}

だからあなたの場合:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
  txt += serialPort1.ReadExisting().ToString();
  SetText(txt.ToString());
}
于 2012-05-27T16:15:14.590 に答える
58

これで十分かどうかはわかりませんが、静的な ThreadHelperClass クラスを作成し、次のように実装しました。これで、多くのコーディングなしで、さまざまなコントロールのテキスト プロパティを簡単に設定できるようになりました。

public static class ThreadHelperClass
{
    delegate void SetTextCallback(Form f, Control ctrl, string text);
    /// <summary>
    /// Set text property of various controls
    /// </summary>
    /// <param name="form">The calling form</param>
    /// <param name="ctrl"></param>
    /// <param name="text"></param>
    public static void SetText(Form form, Control ctrl, string text)
    {
        // InvokeRequired required compares the thread ID of the 
        // calling thread to the thread ID of the creating thread. 
        // If these threads are different, it returns true. 
        if (ctrl.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            form.Invoke(d, new object[] { form, ctrl, text });
        }
        else
        {
            ctrl.Text = text;
        }
    }
}

コードの使用:

 private void btnTestThread_Click(object sender, EventArgs e)
 {
    Thread demoThread =
       new Thread(new ThreadStart(this.ThreadProcSafe));
            demoThread.Start();
 }

 // This method is executed on the worker thread and makes 
 // a thread-safe call on the TextBox control. 
 private void ThreadProcSafe()
 {
     ThreadHelperClass.SetText(this, textBox1, "This text was set safely.");
     ThreadHelperClass.SetText(this, textBox2, "another text was set safely.");
 }
于 2013-04-05T10:12:49.183 に答える
41

これは簡単に実行できます。

TextBox.CheckForIllegalCrossThreadCalls = false;
于 2012-11-26T19:27:42.770 に答える
28

次の拡張機能を使用して、次のようなアクションを渡すだけです。

_frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));

拡張クラス:

public static class CrossThreadExtensions
{
    public static void PerformSafely(this Control target, Action action)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action);
        }
        else
        {
            action();
        }
    }

    public static void PerformSafely<T1>(this Control target, Action<T1> action,T1 parameter)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, parameter);
        }
        else
        {
            action(parameter);
        }
    }

    public static void PerformSafely<T1,T2>(this Control target, Action<T1,T2> action, T1 p1,T2 p2)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, p1,p2);
        }
        else
        {
            action(p1,p2);
        }
    }
}
于 2014-04-24T10:03:58.797 に答える
19

以前の回答と同じ行に沿っていますが、クロススレッド呼び出し例外なしですべてのコントロールプロパティを使用できるようにする非常に短い追加です。

ヘルパー メソッド

    /// <summary>
    /// Helper method to determin if invoke required, if so will rerun method on correct thread.
    /// if not do nothing.
    /// </summary>
    /// <param name="c">Control that might require invoking</param>
    /// <param name="a">action to preform on control thread if so.</param>
    /// <returns>true if invoke required</returns>
    public bool ControlInvokeRequired(Control c,Action a)
    {
        if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
        else return false;

        return true;
    }

サンプル使用法

    // usage on textbox
    public void UpdateTextBox1(String text)
    {
        //Check if invoke requied if so return - as i will be recalled in correct thread
        if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
        textBox1.Text = ellapsed;
    }

    //Or any control
    public void UpdateControl(Color c,String s)
    {
        //Check if invoke requied if so return - as i will be recalled in correct thread
        if (ControlInvokeRequired(myControl, () => UpdateControl(c,s))) return;
        myControl.Text = s;
        myControl.BackColor = c;
    }
于 2014-10-22T11:28:09.073 に答える
7

共有コンテナーを使用して、スレッド間でデータを転送します。

于 2012-05-27T16:09:51.457 に答える