I have a Property of type Observable collection which returns another property collection.When i am enumerating the property from another class by accessing it, I get collection was modified exception.I tried taking lock on the property but it does not seems to work.Any help appreciated
Taking a lock on an object won't do anything unless someone else also takes a lock on the same object. If you absolutely must access this collection from a background thread then you should make sure that both the thread enumerating the collection and the thread modifying the collection have locks on the same objecs.
Its also considered good practice to lock on dedicated locking objects rather than on publicly accessible objects, e.g.
public class MyClass
{
private object _mylock = new object();
private ObservableCollection<string> _myCollection = new ObservableCollection<string>();
public void DoEnumerate()
{
lock (_mylock)
{
foreach (var item in _myCollection)
{
// Do something
}
}
}
public void Modify()
{
lock (_mylock)
{
// Modify the collection here
}
}
}
If you are writing a GUI application then generally it is better to only modify the collection on the UI thread - if you need to do some background processing on the collection then consider taking a copy of the collection (e.g. an array) on the UI thread which the background thread then does its processing with.
You should ensure the ObservableCollection is enumerated end edited from the UI Thread. In order to do so use it like this:
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
MyCollection.Add(new Item());
}
Related
I have created a custom FrameWorkElement ( Battery.cs ) to represent the data to the user in the UI. Within the Battery.cs class I had several dependencyProperties so the UI could monitor the various changes and re-render the object upon changes.
I placed that ObservableCollection within my MainWindowViewModel.cs, which was bound to the main view through a ListBox.
Everything was working properly however this was only for testing as I needed to move the collection down into another class which was going to manage / update the batteries. This management was going to happen asynchronously and thus I was running into a lot of problems with the DependencyProperties calls within the Battery.cs class as they were on the UI thread and not the management/process thread.
So I removed the DependencyProperties, and tried to move the DependencyProperty up to the MainWindowViewModel.cs. Now I am not getting errors about which thread has ownership and I can see that the Batteries in the ObservableCollection are being updated. However the OnRender method is never being called by the UI. So the Batteries are never being rendered/shown anymore.
Here is the code for the DependencyProperty in the MainWindowViewModel.cs
public static readonly DependencyProperty batteriesProperty = DependencyProperty.Register(
"Batteries",
typeof(ObservableCollection<Battery>),
typeof(MainWindow),
new UIPropertyMetadata(new ObservableCollection<Battery>()));
public ObservableCollection<Battery> Batteries
{
get { return tbModel.Modules[0].batteries; }
}
I think my main problem may be in this line
new UIPropertyMetadata(new ObservableCollection<Battery>()));
However I can't seem to figure out what it should be, or how to adjust the code such that the UI does update the graphics once I have called InvalidateVisual within the Battery.cs class.
public void UpdatePacket(Packet packet)
{
packet= packet;
Voltage = packet.Voltage;
InvalidateVisual();
}
The InvalidateVisual() method is executing however the OnRender override is never being executed.
Making ViewModel derived from DependecyObject is pointless. This only complicates and confuses the implementation.
The Batteries property of type ObservableCollection must be a regular CLR read-only property in the ViewModel.
public ObservableCollection<Battery>() Batteries {get;}
= new ObservableCollection<Battery>();
If an instance of the ViewModel exists in the entire session of the Application, then in the ViewModel constructor, synchronize the bindings to this collection.
protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
public MainWindowViewModel()
{
if (Dispatcher.CheckAccess())
{
BindingOperations.EnableCollectionSynchronization(Batteries, ((ICollection)Batteries).SyncRoot);
}
else
{
Dispatcher.Invoke(()=>BindingOperations.EnableCollectionSynchronization(Batteries, ((ICollection)Batteries).SyncRoot));
}
// Some Code
}
If the instances of the ViewModel can be destroyed, replace each other, then the synchronization of the bindings retains a reference to the instance of the ViewModel and therefore this instance will not be deleted by the GC. Also, the synchronization of bindings does not always provide thread safety for working with a collection.
In these cases, you are not using BindingOperations.EnableCollectionSynchronization ().
But instead, you always work with the collection only in the Dispatcher thread.
protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
public MainWindowViewModel()
{
// Some Code
}
... SomeMethod(...)
{
// Some Code
if (Dispatcher.CheckAccess())
{
Batteries.Add(...);
}
else
{
Dispatcher.Invoke(()=>Batteries.Add(...));
// Or
Dispatcher.InvokeAsync(()=>Batteries.Add(...));
}
// Some Code
}
I have an application where items being added to collections from multiple threads.
Randomly i get an
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread. at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
Now the collections are created in a class , the classes themselves are created on multiple threads.
Here is a class example
public class Example
{
public Example()
{
BindingOperations.EnableCollectionSynchronization(collection, COLLECTION_LOCK);
var defaultView = CollectionViewSource.GetDefaultView(collection);
defaultView.SortDescriptions.Add(new SortDescription("SomeProperty", ListSortDirection.Ascending));
if (defaultView is ICollectionViewLiveShaping liveShaping)
liveShaping.IsLiveSorting = true;
}
private readonly object COLLECTION_LOCK = new object();
private readonly ObservableCollection<object> collection = new ObservableCollection<object>();
public ObservableCollection<object> Collection
{
get
{
return collection;
}
}
private void AddItem(object item)
{
lock(COLLECTION_LOCK)
{
if(!Collection.Contains(item))
{
Collection.Add(item);
}
}
}
private void RemoveItem(object item)
{
lock (COLLECTION_LOCK)
{
if (Collection.Contains(item))
{
Collection.Remove(item);
}
}
}
}
I am using the BindingOperations.EnableCollectionSynchronization to allow cross thread operations and always use the specified lock to modify collection.
Still the error comes up randomly.
I have also tried to use BindingOperations.AccessCollection when accessing the collection but the error still happens randomly.
The MS documentation states that ObservableCollection must be created on a UI thread? Can someone confirm that its the case?
Also you can notice that i get the default collection view CollectionViewSource.GetDefaultView(collection)
The collection view is also created on same thread and technically as i understand its the source of the problem.
I have tried to simulate adding from different threads by creating thousands of tasks and modifying the collection with no error happening BUT again randomly error pops up out of nowhere, i tested with both where collection was not bound and bound to UI.
Any ideas?
Stack trace
System.NotSupportedException: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
at System.Collections.ObjectModel.ObservableCollection`1.RemoveItem(Int32 index)
at System.Collections.ObjectModel.Collection`1.Remove(T item)
at Manager.ViewModels.HostViewModelBase.RemoveUser(IUserMemberViewModel user)
The collection view flags are
System.Windows.Data.CollectionView.CollectionViewFlags.ShouldProcessCollectionChanged | System.Windows.Data.CollectionView.CollectionViewFlags.IsCurrentBeforeFirst | System.Windows.Data.CollectionView.CollectionViewFlags.IsCurrentAfterLast | System.Windows.Data.CollectionView.CollectionViewFlags.IsDynamic | System.Windows.Data.CollectionView.CollectionViewFlags.AllowsCrossThreadChanges | System.Windows.Data.CollectionView.CollectionViewFlags.CachedIsEmpty
and AllowsCrossThreadChanges is true
One of the best ways to deal with that is to ditch ObservableCollection alltoghether. Its use case is very narrow and it's hard to work around the Dispatcher issue.
Go with DynamicData instead - once you get the hang of it, it becomes very powerfull and so natural to use:
ReadOnlyObservableCollection<TradeProxy> data;
var source = new SourceCollection<YourClass>();
source.Connect()
.Sort(SortExpressionComparer<YourClass>.Descending(t => t.SomeProperty))
.ObserveOnDispatcher() //ensure operation is on the UI thread
.Bind(out data) //Populate the observable collection
.Subscribe();
// you can do that in ANY THREAD you want and the view will update without any problems:
source.Add(yourClasse);
DynamicData also has filtering with very easy reaplying of the filter, paging, grouping,.... so many things. It is based on Rx, so on top of that you can easily throtle the change when working with big sets, and then make it all instant in UnitTests.
How about implementing a thread safe wrapper of the ObservableCollection?
public class ObservableCollectionWrapper<T> : ICollection<T>, INotifyCollectionChanged
{
private readonly ObservableCollection<T> _collection;
private readonly Dispatcher _dispatcher;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public ObservableCollectionWrapper(ObservableCollection<T> collection, Dispatcher dispatcher)
{
_collection = collection;
_dispatcher = dispatcher;
collection.CollectionChanged += Internal_CollectionChanged;
}
private void Internal_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
_dispatcher.Invoke(() =>
{
this.CollectionChanged?.Invoke(sender, e);
});
}
public int Count => _collection.Count;
/* Implement the rest of the ICollection<T> interface */
}
Usage example:
var collectionWrapper = new ObservableCollectionWrapper<object>(collection, this.Dispatcher);
var defaultView = CollectionViewSource.GetDefaultView(collectionWrapper);
After I tried lots and lots of solutions I couldn't solve this problem by any means so I started to believe that there is no solution for this problem.
I have an object that contains complex attributes. E.g: List<SomeComplexObject>. I am running a method from this class on a worker thread to keep the GUI running until the worker thread finishes. When it finishes execution, I want to use the attributes of these objects to update GUI let's say I want to use List<SomeComplexObject> looping through this list and update the GUI. But each time I try to access this list the debugger throws an InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
I tried to make all attributes of this class volatile but with no hope I also used Lazy<T> class approach to solve but the same problem occurs.
Class that contain the worker function:
public class MainModules
{
#region Attributes
public VIDEO video;
public string VideoPath
{
get;
set;
}
LowLevelModule lowLevelOutput;
//this list that I want to use to Update GUI
public volatile List<FaceRecognitionModule> faceModuleOutput;
//worker function running on different thread
public void RunMainModules()
{
//some complex work to set the class attributes
}
}
Thread creation in GUI class
private void RunMainModules_BtnClick(object sender, RoutedEventArgs e)
{
// MainModule = new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
this.LazyMainModule = new Lazy<MainModules>(this.InitLazyMainModule);
MainModuleThread = new Thread(this.RunMainModules);
MainModuleThread.Start(MainModule);
}
public MainModules InitLazyMainModule()
{
return new MainModules(mainModuleObj, Inpath, lif, keyframefolderpath, trdbpath, labelspath, rrankspath, alignmatpath, 11, 10);
}
public void RunMainModules(Object obj)
{
//MainModules mm = obj as MainModules;
MainModules mm = LazyMainModule.Value;
mm.RunMainModules();
this.Dispatcher.Invoke((Action)(() =>
{
this.InitSpeechRec_Btn.IsEnabled = true;
}));
}
When I try to access faceModuleOutput in class MainModules from GUI I got InvalidOperationException.
Image img = new Image();
//InvalidOperationException occurs here
img.Source = LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;
To brief this post:
I want to access an object instantiated by a background thread from main thread but it throws
InvalidOperationException : The calling thread cannot access this object because a different thread owns it.
A UI control needs to be created/modified from the GUI Thread. Doing otherwise is illegal.
It seems that the MainModuleThread is (at least) creating and modifying an Image . This should be done in the GUI Thread (the one that called RunMainModules_BtnClick)
You cannot modify or even access pretty much anything that relates to the UI thread from another thread. This can get pretty extreme/annoying sometimes because you can't even get the value in a textbox or check if a checkbox is checked or not. If you want to perform an action on an object owned by the UI thread you need to invoke the UI thread to do it.
UIObject.Dispatcher.Invoke(() => {
//[Perform your action in here]
});
Finally I found the solution ... Class BitmapImage is thread-affine so it can't be accessed by multiple threads you need first to make it opened for reading only closed for writing so the compiler can guarantee that no threads will modify it's content
So the solution ... :
//keyframe here is a BitmapImage so on creation we must call keyframe.Freeze()
LazyMainModule.Value.faceModuleOutput[0].keyframes[1].keyframe;
class KeyFrame:
public class KeyFrame
{
public volatile BitmapImage keyframe;
public volatile List<string> personsNames;
public volatile List<string> categories;
public KeyFrame(BitmapImage keyframe, List<string> personsNames, List<string> categories)
{
this.keyframe = keyframe;
//here we call Freeze funcition on creation to make it modifiable
this.keyframe.Freeze();
this.personsNames = personsNames;
this.categories = categories;
}
}
I've written a WPF WizardFramework which performs some actions in the background using some BackgroundWorker. While processing it can happen that I have to update an ObservableCollection which is bound to my UI.
For this case I've written a ThreadableObservableCollection, which provides threadsafe methods for Insert, Remove and RemoveAt. Though I'm using .NET 4.5 I was not able to get BindingOperations.EnableCollectionSynchronization working without many other invalid access exceptions. My Collection looks like:
public class ThreadableObservableCollection<T> : ObservableCollection<T>
{
private readonly Dispatcher _dispatcher;
public ThreadableObservableCollection()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
public void ThreadsafeInsert(int pos, T item, Action callback)
{
if (_dispatcher.CheckAccess())
{
Insert(pos, item);
callback();
}
else
{
_dispatcher.Invoke(() =>
{
Insert(pos, item);
callback();
});
}
}
[..]
}
This is working as expected, while I am using the wizard in my application. Now I'm using NUnit to write some integrationtests for the application.
There's a listener which waits for the WizardViewModel to finish it's work and looking for some pages which are injected in the Steps-Collection. After the asyncrone work is done I can use Validate to check the viewmodel state.
Unfortunately I'm using a ManualResetEvent to wait for the wizard to close. This looks like following:
public class WizardValidator : IValidator, IDisposable
{
private WizardViewModel _dialog;
private readonly ManualResetEvent _dialogClosed = new ManualResetEvent(false);
[..]
public void ListenTo(WizardViewModel dialog)
{
_dialog = dialog;
dialog.RequestClose += (sender, args) => _dialogClosed.Set();
dialog.StepsDefaultView.CurrentChanged += StepsDefaultViewOnCurrentChanged;
_dialogClosed.WaitOne();
}
[..]
}
Now there's a problem:
While the Application is running the UI Thread is not blocked, the Collection can be updated without any problems. But in my testcases the "main" Thread where I initialize the ViewModel (and because of that the Collections) is an AppDomainThread which is blocked by the testcode. Now my ThreadsafeInsert wants to update the collection but cannot use the AppDomain Thread.
But I have to wait for the wizard to finish, how can I solve this kind of deadlock? Or is there a more elegant solution for this one?
edit:
I worked around this problem with a check if there's a user interface, and only then I invoke on the Application-Thread, otherwise I change the collection intentionally on another thread. This does not prevent the exception, but it is not recognized from the test... the items are inserted nevertheless, only the NotifyCollectionChanged-Handler is not called (which is only used in the UI anyway).
if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
Steps.Insert(pos, step);
stepsView.MoveCurrentTo(step);
});
}
else
{
new Action(() => Steps.Insert(pos, step)).BeginInvoke(ar => stepsView.MoveCurrentToPosition(pos), null);
}
This is an ugly workaround and I am still interested in a clean solution.
Is there a way to use an alternate Dispatcher to create (e.g.) the whole ViewModel and use this to change my collection?
As I see the main problem that main thread is blocked and other operations are trying to be executed in main thread too? What about not to block main thread, like this:
// helper functions
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
// in your code:
while(!_dialogClosed.WaitOne(200))
DoEvents();
If it will not help then I guess need to try some SynchronisationContext workarounds.
I think the problems boil down to the fact that you create ObservableCollection that is tied to Dispatcher object.
Involving Dispatcher object directly is almost never good idea(as you just witnessed). Instead I would suggest you to see how others have implemented ThreadSafeObservableCollection. This is a little example I put together, it should illustrate the point:
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lock = new object();
public ThreadSafeObservableCollection()
{
BindingOperations.CollectionRegistering += CollectionRegistering;
}
protected override void InsertItem(int index, T item)
{
lock (_lock)
{
base.InsertItem(index, item);
}
}
private void CollectionRegistering(object sender, CollectionRegisteringEventArgs e)
{
if (e.Collection == this)
BindingOperations.EnableCollectionSynchronization(this, _lock);
}
}
I am new to event and delegates. How can I implement an enqueued event for an object of type Queue<T>?
I am using C# and .Net 4.0
You can encapsulate the Queue class with your own class, something like:
class MyQueue<T>
{
private readonly Queue<T> queue = new Queue<T>();
public event EventHandler Enqueued;
protected virtual void OnEnqueued()
{
if (Enqueued != null)
Enqueued(this, EventArgs e);
}
public virtual void Enqueue(T item)
{
queue.Enqueue(item);
OnEnqueued();
}
public int Count
{
get
{
return queue.Count;
}
}
public virtual T Dequeue()
{
T item = queue.Dequeue();
OnEnqueued();
return item;
}
}
There are no events fired from the System.Collections.* suite of classes. Since you're using .NET 4.0, you may want to look into BlockingCollection<T> instead which, instead of relying on events, you would use the Producer-Consumer pattern to Take elements from the collection as they arrive from another thread. BlockingCollection<T> will take care of all thread-safety and synchronization for you efficiently.
The default backing type for BlockingCollection<T> is ConcurrentQueue<T> which sounds like what you want, but it should be noted that you can change it to use a ConcurrentStack<T> or ConcurrentBag<T> if you want/don't mind different ordering characteristics.
Another great feature of BlockingCollection<T> is the ability to set bounds which can help block the producer from adding more items to the collection than the consumers can keep up with.
For a great write up on all aspects of this subject, I suggest checking out this blog post from Alexeandra Rusina. The post also covers ways to work with BlockingCollection using the Task Parallel Library.