Invalid cross-thread call.

gpoch

Active member
Joined
Oct 28, 2006
Messages
34
Location
USA
Programming Experience
1-3
Hello again,

I'm currently stuck...

I'm receiving data from a serial port. So I process the data, create a custom object based on the data, and then place the object into an inherited ibinding list. My object also implements INotifyPropertyChanged interface so it can easily bind to a datagridview control. Everything works great except the debugger throws an error stating that the datagridview control was created on another thread. I do know that the port.DataReceived event runs on another thread but shouldn't it throw the error on my list, wouldn't the list also be created on a different thread then the serial port.

I've seen some examples of how to make safe cross-thread calls using delegates and the control.invoke method. But is it even possible to make safe thread call to a datagridview control while it is bound to a List?:confused:

Thanks for your help.
 
Please post in the most appropriate forum. This question has nothing to do with the VS IDE and everything to do with Windows Forms. Moved.

The DataGridView is a control and you access members of all controls from a worker thread in exactly the same way: with a delegate. The fact that it's a dataGridView and whether or not it's data-bound has no significance whatsoever. Test its InvokeRequired propery, call its Invoke method and pass it a delegate and, optionally, and data required. That's the pattern no matter the control, method or data.
 
Thanks for the reply. I've read up on this topic and still have some problems. Since the datagridview follows my QueuedCalls list I'm testing its invokeRequired property before adding the msg object to the QueuedCalls list. I don't think I'm doing this right because I can't get a true result from the invokerequired method. Then I get the cross-thread error on the else statement.
VB.NET:
[SIZE=2][COLOR=#0000ff]Delegate [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE][SIZE=2] SetCallback([/SIZE][SIZE=2][COLOR=#0000ff]ByVal[/COLOR][/SIZE][SIZE=2] msg [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] MsgCall)[/SIZE]
 
[SIZE=2][COLOR=#0000ff]Public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE][SIZE=2] AddIncommingCall([/SIZE][SIZE=2][COLOR=#0000ff]ByVal[/COLOR][/SIZE][SIZE=2] msg [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] MsgCall)[/SIZE]
 
[SIZE=2][COLOR=#0000ff]If [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Me[/COLOR][/SIZE][SIZE=2].QueuedGrid.InvokeRequired [/SIZE][SIZE=2][COLOR=#0000ff]Then[/COLOR][/SIZE]
[SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] d [/SIZE][SIZE=2][COLOR=#0000ff]As [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]New[/COLOR][/SIZE][SIZE=2] SetCallback([/SIZE][SIZE=2][COLOR=#0000ff]AddressOf[/COLOR][/SIZE][SIZE=2] DelIncommingCall)[/SIZE]
[SIZE=2][COLOR=#0000ff]Me[/COLOR][/SIZE][SIZE=2].QueuedGrid.Invoke(d, msg)[/SIZE]
[SIZE=2][COLOR=#0000ff]Else[/COLOR][/SIZE]
[SIZE=2]InterCom.QueuedCalls.Add(msg)[/SIZE]
[SIZE=2][COLOR=#0000ff]End[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]If[/COLOR][/SIZE]
 
[SIZE=2][COLOR=#0000ff]End[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE]
 
[SIZE=2][COLOR=#0000ff]Private[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE][SIZE=2] DelIncommingCall([/SIZE][SIZE=2][COLOR=#0000ff]ByVal[/COLOR][/SIZE][SIZE=2] Msg [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] MsgCall)[/SIZE]
[SIZE=2]QueuedCalls.Add(Msg)[/SIZE]
[SIZE=2][COLOR=#0000ff]End[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE]
 
You shouldn't be invoking DelIncomingCall from AddIncomingCall. The pattern is ALWAYS the same. Test the control's InvokeRequired property and, if it's True then use a delegate to invoke the SAME METHOD again on the UI thread. That means that AddIncomingCall should only be invoking AddIncomingCall. If InvokeRequired is False then you do all the work, which may include calling other methods.
VB.NET:
Private Delegate Sub IncomingCallDelegate(ByVal msg As MsgCall)

Public Sub [U][B]AddIncomingCall[/B][/U](ByVal msg As MsgCall)
    If Me.queueGrid.InvokeRequired Then
        'This is a worker thread.
        Me.queueGrid.Invoke(New IncomingCallDelegate(AddressOf [U][B]AddIncomingCall[/B][/U]), msg)
    Else
        'This is the UI thread so perform the operation involving the grid here.
        'If you don't access the grid then why did you call its Invoke method?
    End If
End Sub
Note that if the method is called from a worker thread a delegate is created and it reinvokes the SAME METHOD on the UI thread. The whole point is to get to the UI thread to access the members of a control, specifically the grid, yet your code doesn't access the grid at all once it gets to the UI thread.
 
Thanks again for the reply. That makes total sense now. I don't know why but the control will never show invokeRequired true. If the control is bound to my List, wouldn't it be ok to override the OnListChange method and switch over to the UI thread there. I found how to do an AsyncOperation and it works great, just curious if there is anything wrong with this approach?

VB.NET:
[SIZE=2][COLOR=#0000ff]Private[/COLOR][/SIZE][SIZE=2] _oper [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] AsyncOperation[/SIZE]
 
[SIZE=2][COLOR=#0000ff]Public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]New[/COLOR][/SIZE][SIZE=2]()[/SIZE]
[SIZE=2]_oper = AsyncOperationManager.CreateOperation([/SIZE][SIZE=2][COLOR=#0000ff]Nothing[/COLOR][/SIZE][SIZE=2])[/SIZE]
[SIZE=2][COLOR=#0000ff]End [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE]
 
[SIZE=2][COLOR=#0000ff]Protected [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Overrides [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE][SIZE=2] OnListChanged([/SIZE][SIZE=2][COLOR=#0000ff]ByVal[/COLOR][/SIZE][SIZE=2] e [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] System.ComponentModel.ListChangedEventArgs)[/SIZE]
 
[SIZE=2][COLOR=#0000ff]If[/COLOR][/SIZE][SIZE=2] System.Threading.Thread.CurrentThread.IsBackground [/SIZE][SIZE=2][COLOR=#0000ff]Then[/COLOR][/SIZE]
[SIZE=2]_oper.Post([/SIZE][SIZE=2][COLOR=#0000ff]New[/COLOR][/SIZE][SIZE=2] System.Threading.SendOrPostCallback([/SIZE][SIZE=2][COLOR=#0000ff]Address Of[/COLOR][/SIZE][SIZE=2] PostCallBack), e)[/SIZE]
[SIZE=2][COLOR=#0000ff]Else[/COLOR][/SIZE]
[SIZE=2][COLOR=#0000ff]My Base[/COLOR][/SIZE][SIZE=2].OnListChanged(e)[/SIZE]
[SIZE=2][COLOR=#0000ff]End [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]If[/COLOR][/SIZE]
[SIZE=2][COLOR=#0000ff]End [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE]
 
[SIZE=2][COLOR=#0000ff]Private [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE][SIZE=2] PostCallBack([/SIZE][SIZE=2][COLOR=#0000ff]ByVal[/COLOR][/SIZE][SIZE=2] state [/SIZE][SIZE=2][COLOR=#0000ff]As [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Object[/COLOR][/SIZE][SIZE=2])[/SIZE]
 
[SIZE=2][COLOR=#0000ff]Dim[/COLOR][/SIZE][SIZE=2] args [/SIZE][SIZE=2][COLOR=#0000ff]As[/COLOR][/SIZE][SIZE=2] ListChangedEventArgs = [/SIZE][SIZE=2][COLOR=#0000ff]CType[/COLOR][/SIZE][SIZE=2](state, ListChangedEventArgs)[/SIZE]
[SIZE=2][COLOR=#0000ff]MyBase[/COLOR][/SIZE][SIZE=2].OnListChanged(args)[/SIZE]
[SIZE=2][COLOR=#0000ff]End [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]Sub[/COLOR][/SIZE]
 
Last edited:
Back
Top