-3

If I try to execute this code in my main WindowsForm, I get the following exception:

An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll Additional information: Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.

I found a lot about events, eventhandler and threads. However I never really worked deeply with events and how to manually create them or multi threading. I found this article on MSDN

--> Link <-- but I did not really understand it. The error appears if try to write my output into the richtextbox1.

SerialPort Arduino = new SerialPort();
    public Form1()
    {
        InitializeComponent();
        this.Load += Form1_Load;

    }

    void Form1_Load(object sender, EventArgs e)
    {
      string[] k = SerialPort.GetPortNames();
      cBPortWaehlen.DataSource = k;

    } 
    private void btnOpenPort_Click(object sender, EventArgs e)

    {
        if (!Arduino.IsOpen)
        {
            Arduino.DataReceived += Arduino_DataReceived;
            Arduino.BaudRate = 115200;
            Arduino.PortName = cBPortWaehlen.SelectedItem.ToString();
            Arduino.Open();
        }
        else
        {
            MessageBox.Show("Port schon offen");
        }

    }

    private void Arduino_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
            this.richTextBox1.AppendText(Arduino.ReadExisting());

    }

    private void btnClosePort_Click(object sender, EventArgs e)
    {
        Arduino.Close();

    }

2 답변


3

When you receive data in

private void Arduino_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
        this.richTextBox1.AppendText(Arduino.ReadExisting());

}

That event originates from a different thread, which is monitoring your Arduino.

By default, your winforms application has at least one thread (the UI thread). If you execute code on that thread, it will halt the UI, making it unresponsive.

So if you want things to happen in the background while your UI remains responsive, this must be done in a separate thread.

Unfortunately (but for sevaral practical reasons), threads cannot use each other's references.

They can however send each other messages.

One of these is a request to invoke a specific action. Windows Forms has a couple of handy methods built in in order to make use of this :

private void Arduino_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
        if(richTextBox1.InvokeRequired)
        {
            richTextBox1.Invoke(
               (Action)delegate 
               { 
                 richTextBox1.AppendText(Arduino.ReadExisting()); 
               }
            );
        }
}


  • Why are you checking if an invoke is required when you know that an invoke is required? It's like writing if(true) in your code. - Servy
  • @Servy It'd be interesting to know if the compiler / jitter realised that and removed the statement... - James Thorpe
  • Of course it can't. It can't know that the event is always fired from another thread even though you do. - Servy
  • @Servy Ah yes, of course. I think it was the thought of it optimising away the if (true) that made me think that :) - James Thorpe

1

make sure that you're updating your form with the UI thread only. every time when you set access a property of a UI component (e.g. this.richTextBox1.AppendText) take care that you're delegating it to the UI thread to avoid the Cross-thread exception.

you could do something like:

delegate void UpdateDelegate(string text);

private void UpdateInformation(string text)
{
   if(this.InvokeRequired)
   {
      UpdateDelegate ud = new UpdateDelegate(UpdateInformation);
      this.BeginInvoke(ud, new object[] { text } );
   }
   else 
   {
      this.myTextBox.Text = text;
   }
}

you could also use anonym delegate, but the thing above might be easier to understand.

Linked


Related

Latest