Неверная операция поперечного потока: элемент управления 'textBox1' доступен из потока, отличного от потока, который был создан на

Я хочу отправить значение температуры с микроконтроллера, используя интерфейс UART на С# и температуру дисплея на 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);
                }
     }

и мой код С#:

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

но возникает исключение " Работа с несколькими потоками недействительна: элемент управления 'textBox1' доступен из потока, отличного от потока, который был создан на Скажите, пожалуйста, как получить строку температуры от моего микроконтроллера и удалить эту ошибку!

Ответ 1

Данные, полученные в вашем методе serialPort1_DataReceived, поступают из другого контекста потока, кроме потока пользовательского интерфейса, и причина, по которой вы видите эту ошибку.
Чтобы исправить это, вам придется использовать диспетчер, как описано в статье MSDN:
Как сделать безопасные вызовы для Windows Forms Controls

Поэтому вместо того, чтобы устанавливать свойство text непосредственно в методе 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());
}

Ответ 2

Я не знаю, достаточно ли это, но я создал статический класс 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.");
        }

Ответ 3

вы можете просто сделать это.

TextBox.CheckForIllegalCrossThreadCalls = false;

Ответ 4

Используйте следующие расширения и просто передайте действие, например:

_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);
        }
    }
}

Ответ 5

В том же направлении, что и предыдущие ответы, но очень короткое дополнение, которое позволяет использовать все свойства Control без исключения перекрестного потока invokation.

Метод помощника

    /// <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;
    }

Ответ 6

Использовать общий контейнер для передачи данных между потоками.