Click here to Skip to main content
15,899,754 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Need help! Can't figure out what's going on here. I have a asynchronous bindingList that holds objects. This asynchronous bindingList is created on a worker thread and objects are added, updated and removed on the worker thread. On the Main UI thread my bindingList is bound to a bindingSource that's bound to a dataGridView.

I keep getting "System.Reflection.TargetInvocationException" and index out of bounds exception.
Can someone please help me out here?

Here's the last exception I got:
System.Reflection.TargetInvocationException was unhandled
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
       at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
       at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
       at System.Delegate.DynamicInvokeImpl(Object[] args)
       at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
       at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
       at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.DataGridView.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at StrategyViewer.Program.Main() in C:\C Sharp Applications\StrategyViewer\StrategyViewer\Program.cs:line 18
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: System.IndexOutOfRangeException
       Message=Position is either less than 0 or greater than the number of items in the data source.
       Source=System.Windows.Forms
       StackTrace:
            at System.Windows.Forms.CurrencyManager.ChangeRecordState(Int32 newPosition, Boolean validating, Boolean endCurrentEdit, Boolean firePositionChange, Boolean pullData)
            at System.Windows.Forms.CurrencyManager.List_ListChanged(Object sender, ListChangedEventArgs e)
            at System.Windows.Forms.BindingSource.OnListChanged(ListChangedEventArgs e)
            at System.Windows.Forms.BindingSource.InnerList_ListChanged(Object sender, ListChangedEventArgs e)
            at System.ComponentModel.BindingList`1.OnListChanged(ListChangedEventArgs e)
       InnerException: 

I getting the impression that this exception is being throw on my worker thread because of my bindingList manipulations.
Posted
Updated 8-Mar-12 9:47am
v2

You really should not modify a data bound object on a background thread in such a way that it updates the UI. If you don't get TargetInvocationException, you will get warnings about cross-thread calls. Any attempt to Add, modify or Remove items in the BindingList is going to trigger the UI to update itself from a background thread, and this is a no-no.
 
Share this answer
 
Comments
d.allen101 8-Mar-12 16:35pm    
so how do I resolve this. From my understanding this (asyncBindingList) is suppose to be thread safe...here's the code:


public class AsyncBindingList<t> : BindingList<t>
{
ISynchronizeInvoke invoke;

public AsyncBindingList(ISynchronizeInvoke invoke)
{
this.invoke = invoke;
}

delegate void ListChangedDelegate(ListChangedEventArgs e);

protected override void OnListChanged(ListChangedEventArgs e)
{
if (invoke.InvokeRequired)
{
IAsyncResult asyncResult = invoke.BeginInvoke(new ListChangedDelegate(base.OnListChanged), new object[] { e });
}
else
{
base.OnListChanged(e);
}
}
}
Thread-safe means that multiple threads can work with it at the same time without requiring you to do anything special, like creating locks or critical sections. So multiple threads could add to a list at the same time and not cause problems. While your collection may be thread safe, controls are not. If controls are data-bound, then modifying the data is reflected in the UI instantly. Data binding is nice but difficult when working with multiple threads.

BackgroundWorker is the preferred way to work with background threads that modify UI data. You do your work in DoWork, and update your collection / object / UI in RunWorkerCompleted. It is possible to get what you want from a regular thread though, here's how you do it:

The Control class has a property called InvokeRequired. This is a boolean, it will always be false from the UI thread and it will always be true from a background thread. This is one of the few properties of Control that you can access from a background thread. If you wanted to update a Label's Text from a thread, you would have a function like this:

C#
Delegate void UpdateTextCallback(String text);

void UpdateLabelText(String text)
{
    // if invoke required, tell label to execute this on its thread
    if (Label1.InvokeRequired)
    {
        UpdateTextCallback callback = new UpdateTextCallback(UpdateLabelText);
        Label1.Invoke(callback, new Object() { text });
    }
    else // we are on the correct thread, set label text
    {
        Label1.Text = text;
    }
}


In your case, you can check your DataGridView.InvokeRequired and if so add an item to the collection using a similar pattern above. I strongly recommend using BackgroundWorker though, it is setup to do the kind of things that it seems like you want to do.
 
Share this answer
 
Comments
d.allen101 8-Mar-12 21:58pm    
isn't your solution the exact thing i'm already doing? I pass the control(dataGridView) to the AsyncBindingList constructor i.e. "public AsyncBindingList(ISynchronizeInvoke invoke)" which is on my worker thread i.e. backGroundThread and my asyncBindingList over-rides "protected override void OnListChanged(ListChangedEventArgs e)" and then applies:

if(InvokeRequried)
{
IAsyncResult asyncResult = invoke.BeginInvoke(new ListChangedDelegate(base.OnListChanged), new object[] { e });
}
else
{
base.OnListChanged(e);
}
Hmm, I've never used an asychronous binding list but now that you mention it, you're right. I never noticed that InvokeRequired and Invoke are part of the ISynchronizeInvoke interface (learn something new every day).

So, I went to the MSDN page on ISynchronizeInvoke interface and I see this that might help you:

http://msdn.microsoft.com/en-us/library/system.componentmodel.isynchronizeinvoke.aspx[^]

1. Asynchronously, by using the BeginInvoke method. BeginInvoke starts a process and then returns immediately. Use EndInvoke to wait until the process started by BeginInvoke completes.

2. Synchronously, by using the Invoke method. Invoke starts a process, waits until it completes, and then returns. Use Invoke when the control's main thread is different from the calling thread to marshal the call to the proper thread.

Situation 2 definitely applies to you. Try using Invoke instead of BeginInvoke. I hope this works for you, let me know because I may start using collections like this myself.
 
Share this answer
 
v2
Comments
d.allen101 8-Mar-12 23:59pm    
thx patrick. i'm going to give a try when i get into the office tomorrow. I've been using the first option (beginInvoke) sounds like i should have been using option 2 Invoke since my control (ISynchronizedInvoke) is on the main UI thread and my bindingList is on the worker thread
d.allen101 9-Mar-12 0:20am    
hey patrick I think calling beginInvoke may actually be my problem. I'm thinking that the my threading is probably modifying the bindingList faster than the gui is updating and that's what's causing exceptions i've been seeing (index out of range and System.Reflection.TargetInvocationException
Patrick Fox 9-Mar-12 0:25am    
Yes, that was my thought too. The inner exception points to that. Hope it works! I'm going to try it too, because I use data binding a lot and I've been doing silly things. Like setting DataSource to null, doing background work,then resetting data source, or wrapping any type of Add / Update / Delete in similar methods above. Now I realize I could have been wrapping the Invoke at the collection level the whole time...
d.allen101 9-Mar-12 9:57am    
hey Patrick. THANKS!!!!! i'm at work testing it, and its working like a charm! you were exactly right. i was using "beginInvoke" and should have been using "Invoke". THANK YOU! i'm soooo greatful!
Patrick Fox 9-Mar-12 10:51am    
Awesome, glad to hear it. I've been converting my collection classes to do this too, this is the better way to work with asynchronous binding. Seems we both gained something useful :)
Sorry to wake up your question.
I was already using such asyncBindingList in my implementation.
As i start a new implementation, i choose to not use it anymore, i didn't remember really the reason for what i am using such class.

So i fall again in the described exception....
And i found your question and solution that describe very well the problem.

But i am a little confused...

For my example, the Data object (so the list that could become asyncbindinglist) have several client on the ListChanged event : bunsines object and the ui...

If am using the Solution3, all event handler will be call from the ui thread ! Also event handler of other business object ! This is really strange ! No ?

For me i think that the problem is more in the implementation of each event handler subscribed to the ListChanged event. The UI should protect itself for the thread problem. Usually, when we are making a manual databinding (as your solution 2), we always take into account this thread problem (as your solution 2 does...). So why the DataGridView does not do that in its event handler implementation in its databinding management ??? It is a bug, a limitation ?

Finally, for me, psychologically, the bette solution should on the event handler implementation, so on the ui side. Yes, the workaround of the asyncbindinglist works, but is uses the ui thread to call ALL event handler !

Maybe the fatest solution should be to have an AsyncBindingList on the ui side. The ui should need to synchronise its AsyncBindingList with the "official" list. Then the datagridview can use this protected AsyncBindingList, dedicated only to it.
In fact, the AsyncBindingList replace the DataSource. (I d'ont understand the utility of this last one, as thread issue is not checked by it...)
But whit this solution, you have your list twice in the memory.... And finally, in place of populate our personal AsyncBindingList in the EventHandler of the official List, the ui could directly modify the datgridview ?

Finally, maybe someone, does a implementation of a datagridview with a "thred protected" DataBinding feature ?
If not, maybe i will add a DataSource to our AsyncBindingListclass to can use it only for the ui context (and not for all business object) ?

What do you think ?
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900