1

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)
Romain
  • 45
  • 6
  • This has nothing to do with .NET Standard, which doesn't bother with the UI. This is raised by your runtime specific code because you tried to modify the UI from another thread and no, you don't need the Dispatcher or Invoke to resolve this. Since 2012 it's *far* easier to use async/await – Panagiotis Kanavos Jul 27 '18 at 11:18
  • And you *definitely* don't need the dispatcher to call something *from* the UI thread. It's the other way around that causes problems. What does your *code* look like? Are you using data binding? Why would you want a *library* to bind controls to ViewModel properties instead of specifying the binding expression? – Panagiotis Kanavos Jul 27 '18 at 11:20
  • why do you want to modify the ObservableCollection from *other threads* anyway? This won't make it faster to add objects, it will make the UI look jerkier as each addition forces a redraw. – Panagiotis Kanavos Jul 27 '18 at 11:26
  • Finally, post the *full* exception as returned by `Exception.ToString()`, not just the type name. There's no code that raises NotSupportedException [in the source code](https://github.com/dotnet/corefx/blob/master/src/System.ObjectModel/src/System/Collections/ObjectModel/ObservableCollection.cs) – Panagiotis Kanavos Jul 27 '18 at 11:30
  • @PanagiotisKanavos It's just what I have to use at work. The collection gets new items, and removed items from multiple network threads. I am updating my question with the exception, but it's the regular one – Romain Jul 27 '18 at 11:49

1 Answers1

0

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

Bizhan
  • 16,157
  • 9
  • 63
  • 101
  • 2
    Dispatcher doesn't exist in .NET Standard 2.0. Besides, it's better to *not* try and modify thread-unsafe collections from different threads or libraries. *Return* the data to the UI thread and let it add them. `Dispatcher.Invoke` does the same with far greater overhead and a high risk of blocking – Panagiotis Kanavos Jul 27 '18 at 11:25
  • How does this answer the question of _"how do I go about modifying theses collections from another thread `without a Dispatcher`? "_ –  Jul 27 '18 at 11:34
  • @MickyD yes I'm sorry I wasn't aware that I undeleted that answer. Anyway, I updated my answer please take a look. – Bizhan Jul 27 '18 at 11:41
  • Your delegate solutions sounds like it could work, I will still wait a bit if anyone has a better solution, otherwise I will accept yours :) – Romain Jul 27 '18 at 11:55