Reputation: 5480
I am using SerialPort to get the Caller ID when someone rings my landline. I've tested it using PuTTy software and it works fine.
My C# code however throws an InvalidOperation exception and says that: Cross-thread operation not valid: Control lblCallerIDTitle
accessed from a thread other than the thread it was created on. This error occurs when I try to do: lblCallerIDTitle.Text = ReadData;
How can I fix this? below is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
namespace CallerID
{
public partial class CallerID : Form
{
public CallerID()
{
InitializeComponent();
port.Open();
WatchModem();
SetModem();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
WatchModem();
}
private SerialPort port = new SerialPort("COM3");
string CallName;
string CallNumber;
string ReadData;
private void SetModem()
{
port.WriteLine("AT+VCID=1\n");
port.RtsEnable = true;
}
private void WatchModem()
{
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
ReadData = port.ReadExisting();
//Add code to split up/decode the incoming data
lblCallerIDTitle.Text = ReadData;
Console.WriteLine(ReadData);
}
private void CallerID_Load(object sender, EventArgs e)
{
}
}
}
I want a label to show the incoming data. Thanks
Upvotes: 0
Views: 2107
Reputation: 50215
You need to use Invoke
when updating the UI from another thread.
Replace...
lblCallerIDTitle.Text = ReadData
... with ...
lblCallerIDTitle.Invoke(new SetCallerIdText(() => lblCallerIDTitle.Text = ReadData));
... after declaring this somewhere in your class ...
public delegate void SetCallerIdText();
Though I like the answer to this question better.
Upvotes: 2
Reputation: 3167
Your user controls (such as the label) have to be updated on the UI thread, and the serial data is coming in on another thread.
Your two options are to either push the control update to the main UI thread with an Invoke(), or have the serial thread update a variable, and use a timer control to check that variable to update the label.
The code for the first (better) option, should look something like this:
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
ReadData = port.ReadExisting();
//Add code to split up/decode the incoming data
if (lblCallerIDTitle.InvokeRequired)
lblCallerIDTitle.Invoke(() => lblCallerIDTitle.Text = ReadData);
else
lblCallerIDTitle.Text = ReadData;
Console.WriteLine(ReadData);
}
The label will then be always updated on the main UI thread.
Upvotes: 3