Click here to Skip to main content
15,877,675 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
In my ViewModel I have a ObservableCollection of type Point which is bound to one of the chart's series within the View. When the user clicks a button the an asynchronous call is made to a method which executes a computationally intensive calculation and updates the ObservableCollection throughout the process.

When it gets to the line that updates an element within the ObservableCollection an InvalidOperationException is thrown with the explanation that "The calling thread cannot access this object because a different thread owns it." I understand that this is occurring because I am trying to change an object which is bound to a control on the UI thread.

How do people typically deal with this problem? I'm hoping there is a very clean and straightforward way? Or do I have to make a call using a delegate to invoke on the ObservableCollection?



Update with more information:

I didn't use the .ConfigureAwait when launching the asynchronous method. Simply:
<br />
await Task.Factory.StartNew(() => methodName(parameter1));


A little more information I gathered while trying to get this to work. Initially I had an ObservableCollection of type System.Windows.Point which has an X and a Y property which is being used in the chart. Because the X and Y do not have a Set attribute I had to create a new Point element looking something like
<br />
observableCollection1[0] = new System.Windows.Point(newPointX, newPointY);<br />


It is at that line where the InvalidOperationException is thrown.

I tried it another way of creating a List of type Point and added each new newXPoint and newYPoint value to the list and then outside the loop created a new ObservableCollection object initialized with the List elements like so:
<br />
observableCollection1 = new ObservableCollection<system.windows.point>(tempList);</system.windows.point>


This does not throw the InvalidOperationException and seems to work fine. I imagine this is probably the better way to do it as well since the NotifyPropertyChanged event will only fire once instead of every time that an element set to a new instance of a Point object. Is that correct??

Regardless of which method is better I am still very confused why I am getting the InvalidOperationException. Any thoughts?
Posted
Updated 11-Feb-15 5:36am
v2
Comments
Kenneth Haugland 9-Feb-15 16:40pm    
Pretty straight forward. Here is one example:
http://www.codeproject.com/Articles/865337/WPF-Apps-Screen

1 solution

If you're using .NET 4.5, you can use the BindingOperations.EnableCollectionSynchronization method[^] to allow you to update the collection from other threads:
C#
private static object _lock = new object();
private readonly ObservableCollection<Point> _points = new ObservableCollection<Point>();

public YourViewModel()
{
    BindingOperations.EnableCollectionSynchronization(_points, _lock);
}

DataBinding Features in WPF 4.5 - DotNetCurry[^]

Alternatively, if you use async / await (without using .ConfigureAwait(false) on your awaitables), the code should always run on the UI thread, and you should never see this exception.
 
Share this answer
 
Comments
MrGlass3 11-Feb-15 11:20am    
I didn't use the .ConfigureAwait when launching the asynchronous method. Simply:
await Task.Factory.StartNew(() => methodName(parameter1));


A little more information I gathered while trying to get this to work. Initially I had an ObservableCollection of type System.Windows.Point which has an X and a Y property which is being used in the chart. Because the X and Y do not have a Set attribute I had to create a new Point element looking something like

observableCollection1[0] = new System.Windows.Point(newPointX, newPointY);


It is at that line where the InvalidOperationException is thrown.

I tried it another way of creating a List of type Point and added each new newXPoint and newYPoint value to the list and then outside the loop created a new ObservableCollection object initialized with the List elements like so:

observableCollection1 = new ObservableCollection<system.windows.point>(tempList);</system.windows.point>


This does not throw the InvalidOperationException and seems to work fine. I imagine this is probably the better way to do it as well since the NotifyPropertyChanged event will only fire once instead of every time that an element set to a new instance of a Point object. Is that correct??

Regardless of which method is better I am still very confused why I am getting the InvalidOperationException. Any thoughts?
Richard Deeming 11-Feb-15 11:27am    
Since you've used Task.Factory.StartNew, the methodName method is running on a background thread, not the UI thread. When it tries to update the collection, the collection changed event fires on that background thread, which WPF doesn't like.

Code that runs after the await line will still be running on the UI thread.

If you can't make methodName an async method, then the BindingOperations.EnableCollectionSynchronization approach is probably simplest.

Otherwise, you'd have to update methodName to not touch the collection, and return the list of changes instead. The calling method would then apply the changes to the collection, which would work since it's running on the UI thread.
MrGlass3 11-Feb-15 11:34am    
I see what you are saying but I am confused at why there is no problem initializing a new ObservableCollection (which the chart is bound to) but there is a problem when initializing a new element within the ObservableCollection.
Richard Deeming 11-Feb-15 11:37am    
Are you definitely raising the PropertyChanged event when you create a new collection?

If so, then it must be a quirk of how WPF collection data-binding works.
MrGlass3 11-Feb-15 11:43am    
Positive. I went in and commented out the line which raises the PropertyChanged event just to make sure and now my chart does not get updated so the PropertyChanged event was definitely firing and updating the chart.

Hmmm this is going to bug me but I think the way I am currently doing it is better than my original plan so at least it works...

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