Reputation: 13
I am new to C# and am looking for some advice on an issue I have been trying to solve in my Windows Form application.
I have an application that needs to continuously read data coming back to the program over a connected serial port. I have buttons that Open and Close the port via the user. I am having trouble configuring the "DataReceived" event handler to read the incoming data and display it in a textbox in the app.
I have been getting this error: "Cross-thread operation not valid: Control 'textBox4' accessed from a thread other than the thread it was created on." I see this is a thread error but I have not been able to figure out my issue.
namespace Program
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
getAvailabePorts();
}
private void getAvailabePorts()
{
String[] ports = SerialPort.GetPortNames();
comboBox1.Items.AddRange(ports);
}
public void button1_Click(object sender, EventArgs e)
{
try
{
if (comboBox1.Text == "" || comboBox2.Text == "")
{
textBox4.Text = "Please select port settings";
}
else
{
serialPort1.PortName = comboBox1.Text;
serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
serialPort1.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
serialPort1.Open();
}
}
catch (UnauthorizedAccessException)
{
textBox4.Text = "Unauthorized Access";
}
public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
textBox4.Text = sp.ReadExisting() + "\n";
}
private void button2_Click(object sender, EventArgs e)
{
serialPort1.Close();
textBox4.Clear();
}
}
}
}
Upvotes: 1
Views: 1957
Reputation: 954
First, welcome.
Before the "big" issue (marshalling data), let me warn you -- serial ports are tricky. For example, your call to "ReadExisting" may not return what you expect -- will return whatever is in the serial port buffer at the time, but more may come in, which will overwrite what is already in your text box. So you may want to append data your text box.
Now the real issue. As a commentor mentioned, you cannot post directly post data from another thread to the UI thread. Without you knowing, the serial port created a new thread to receive data.
You can handle this directly by modifying your receiver code as follows:
public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort) sender;
var dataRcvd = sp.ReadExisting();
object[] dataArray = new object[1];
dataArray[0] = dataRcvd;
BeginInvoke( new postDataDelegate( postData), dataArray );
}
private delegate void postDataDelegate( string d );
private void postData( string d)
{
textBox4.Text = d;
}
This will "marshall" the data to the UI thread so it can be used. There are many ways this can be done (and, many differences between how it is done in WPF vs. Winforms, so watch out for that). I hope this illustrates the point.
Another aside -- no need ot make the DataReceived method public -- it will work fine private.
Upvotes: 2