views:

290

answers:

4

Whenever i am updating UI in windows form using delegate it gives me cross thread exception why it is happening like this? is there new thread started for each delegate call ?

void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
       //this call delegate to display data
       clsConnect(statusMsg);
}




 protected void displayResponse(string resp)
 {
     //here cross thread exception occur if directly set to lblMsgResp.Text="Test";
     if (lblMsgResp.InvokeRequired)
     {
        lblMsgResp.Invoke(new MethodInvoker(delegate { lblMsgResp.Text = resp; }));
     }
 }
A: 

Cross Thread exception happens when some none UI thread changes UI elements. Since UI elements should be changed only in the UI thread this exception is thrown. To help you understand why this happen you will have to publish you code.

Ikaso
code posted above
Neo
If I understand correctly, the Port_DataReceived eventually calls displayResponse. If so, then Port_DataReceived is called from another thread. Does IO API calls this method?
Ikaso
A: 

Cross thread exception happens when some none UI thread changes the UI elements. To fix this use the Invoke method on the control itself. As an extra you can check InvokeRequired on the control before calling the Invoke method See msdn

Bart
+2  A: 

Port_DataReceived is obviously an async event handler that is being raised by a thread on port monitoring component.

is there new thread started for each delegate call ?

No, probably not. Your port monitoring component is running the poll on a background thread and the event is being raised from that thread, every time.

The point is that it is being called on a thread other than the UI, so you will need to use Control.Invoke and the patterns associated with it.

Consider this, (and read the post that may illuminate things for you)

void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
   //this call delegate to display data
   UpdateTheUI(statusMsg);
}

private void UpdateTheUI(string statusMsg)
{
    if (lblMsgResp.InvokeRequired)
    {
        lblMsgResp.BeginInvoke(new MethodInvoker(UpdateTheUI,statusMsg));
    }
    else
    {
       clsConnect(statusMsg);
    }
}

With all of that said, I would be remiss if I didn't point out that the indirection is troubling.

Sky Sanders
Changed `BeginInvoke` to `Invoke`, Hans is right. I overlooked that when I CP coded that.
Sky Sanders
Wrong way around, Invoke() very commonly causes deadlock on the Close() call.
Hans Passant
@Hans, You obviously have more experience dealing with this, I am going to revert my revision, but why don't you add a code sample and lets get the check on your answer. I would feel better about that.
Sky Sanders
+4  A: 

The DataReceived event is always raised on a threadpool thread. You cannot update any UI control, you have to use Control.BeginInvoke(). There is no point testing InvokeRequired, it is always true.

A couple of things to keep in mind here:

  • Don't call Control.BeginInvoke for every single character or byte that you receive. That will bring the UI thread to its knees. Buffer the data you get from the serial port until you've got a complete response. Using SerialPort.ReadLine() usually works well, a lot of devices send strings that are terminated by a line feed (SerialPort.NewLine).
  • Shutting down your program can be difficult. You have to make sure to keep the form alive until the serial port stops sending. Getting an event after the form is closed will generate an ObjectDisposed exception. Use the FormClosing event to close the serial port and start a one second timer. Only really close the form when the timer expires.
  • Avoid using Control.Invoke instead of BeginInvoke. It can deadlock your program when you call SerialPort.Close().

Lots of ways to get in trouble. Consider using your own thread instead using DataReceived to avoid them.

Hans Passant
+1 @Hans: great advice.
Ikaso