0

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

annikins
  • 3
  • 4
Subby
  • 5,370
  • 15
  • 70
  • 125

2 Answers2

3

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.

MCattle
  • 2,897
  • 2
  • 38
  • 54
  • Can you kindly provide a solution for either one as I am fairly new to C#. – Subby Jun 05 '12 at 14:42
  • Hi MCattle. lblCallerIDTitle.Invoke(() => lblCallerIDTitle.Text = ReadData); Gives me the following error: Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type – Subby Jun 05 '12 at 15:01
2

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.

Community
  • 1
  • 1
Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
  • 1
    lblCallerIDTitle.Invoke(() => lblCallerIDTitle.Text = ReadData); Gives me the following error: Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type. – Subby Jun 05 '12 at 14:56
  • Thank you very much for your help. Can you please tell me how I can get the incoming Number as I am always getting the string "RING RING". – Subby Jun 05 '12 at 15:16
  • @Subby: you should ask another question – Austin Salonen Jun 05 '12 at 15:19
  • Okie doke, i'll do that now. Thank you very much for your help! – Subby Jun 05 '12 at 15:21