Search code examples
c#wpfcollections.net-standard.net-standard-2.0

How to modify an ObservableCollection from a different thread in .Net Standard 2.0?


I have a library in .Net Standard 2.0 that is used in multiple projets (Xamarin, WPF, ...), which uses ObservableCollection in order to bind them in WPF or to be able to react to their modifications.

When I try to remove an element from theses collections from a new thread. It then throw a "NotSupportedException", which is usually resolved by using the Dispatcher, to call the remove method from the UI thread.

The problem is that .Net Standard does not provide the Dispatcher (which makes sense, since there might be no UI thread). So this leads to my question; how do I go about modifying theses collections from another thread without a Dispatcher? Am I doing something that I shouldn't do by using ObservableCollections here?

The exception:

System.NotSupportedException: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
à System.Windows.Data.CollectionView.OnCollectionChanged(Object sender,     NotifyCollectionChangedEventArgs args)
à System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
à ALX.GroundStation.Host.Infrastructure.ReadOnlyObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)

Solution

  • You could simply pass the proper dispatcher to the library:

    public void ThreadSafeAddItem(Item item, Dispatcher dispatcher)
    {
        dispatcher.Invoke(()=> { AddItem(item); } );
    }
    

    But since .Net standard 2.0 does not have Dispatcher class you need a delegate to do that in WPF project.

    public event Action<Item> ItemAdded;
    public void ThreadSafeAddItem(Item item)
    {
        if(ItemAdded!=null) ItemAdded(item);
    }
    

    And in your WPF:

     Library.ItemAdded += (item) => {
         Dispatcher.Invoke(()=>Library.ObservCollection.Add(item));
     };
    

    In another thread:

     Library.ThreadSafeAddItem(item);
    

    Am I doing something that I shouldn't do by using ObservableCollections here

    According to MSDN:

    To fully support transferring data values from binding source objects to binding targets, each object in your collection that supports bindable properties must implement an appropriate property changed notification mechanism such as the INotifyPropertyChanged interface.

    That possibly means that every bindable property needs this mechanism as well, creating a huge overhead in your app.

    It is recommended to use a thread-safe approach for the library (like a POCO Model) and instead address these bindings in another tier like ViewModel