19

In a WPF application an ObservableCollection is filled and updated by LINQ to SQL queries. Then UI objects are updated using values from this ObservableCollection.

Is it possible and reasonable that operations of updating this ObservableCollection by LINQ to SQL queries were executed in a separate thread?

If yes, will, in this case, it be one and the same instance of this ObservableCollection? (I mean, if it is not the same one for taking values from LINQ datacontext and the one for giving values to update UI, then I will not be able to update UI)

rem
  • 16,745
  • 37
  • 112
  • 180

4 Answers4

34

.Net 4.5 provides a solution within the BindingOperations class.

You can now use the BindingOperations.EnableCollectionSynchronization method as follows:

private readonly object _personCollectionLock;
private ObservableCollection<Person> _personCollection;

public ObservableCollection<Person> PersonCollection
{
  get { return _personCollection; }
  set
  { 
    _personCollection = value;
    BindingOperations.EnableCollectionSynchronization(_personCollection, _personCollectionLock);
  }

I have only just tried this in my development environment but everything seems to be working correctly now when I update the collection from a background thread.

There is a more in-depth discussion of this solution at: http://10rem.net/blog/2012/01/16/wpf-45-observable-collection-cross-thread-change-notification

The MSDN entry for this method is at: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx

Brian Hinchey
  • 3,601
  • 1
  • 39
  • 36
  • Note that for this to work, the `set` method must be called; if you initialize your backing field inline with its declaration, and never explicitly recreate the collection, this doesn't work. Either move the initialization, or call `EnableCollectionSynchronization` from the constructor too. – Geoff Feb 22 '23 at 18:25
30

With the built-in ObservableCollection<T> class, you can't change the content from a separate thread if the UI is bound to the collection, it throws a NotSupportedException (but change notification for properties of collection items works fine). I wrote an AsyncObservableCollection<T> class to handle this case. It works by invoking the event handlers on the UI synchronization context

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • a posible alternative http://stackoverflow.com/questions/12881489/asynchronously-adding-to-observablecollection-or-an-alternative – Narkha Sep 25 '13 at 15:53
  • Or you can try this which is comletely thread-safe, works from any thread, and can be databound by multiple UI threads : http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection – Anthony Apr 15 '14 at 00:59
  • Nice job on the collection!! – Trae Moore Jun 26 '14 at 17:31
  • @Thomas Levesque how to deal with "ghosts" (probably because the UI is enemerating while content is being added). See https://stackoverflow.com/questions/13624289/wpf-datagrid-is-adding-extra-ghost-row. Do you have a solution for that? Perhaps some locking required? – Werner May 22 '18 at 09:48
  • Brilliant! Very easy to implement. Thank you! – Smitty-Werben-Jager-Manjenson Jul 15 '21 at 14:08
2

Trying to understand your question here:

Scenario 1
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A.
2. Periodically, more data is retrieved from database and added to A. Old data is removed from A.

Scenario 2
1. LINQ to SQL retrieves data set from database and adds to ObservableCollection A.
2. Periodically, data in A is updated with new data from database (no add/remove).

With Scenario 1, you're going to have to use the UI thread. The UI thread owns the ObservableCollection and you'll get an exception if you try to use it in another thread.

With Scenario 2, thumbs up. As long as you don't try to add or remove items from the collection itself, you can update the item as much as you want in a background thread.

apandit
  • 3,304
  • 1
  • 26
  • 32
2

I did not receive the same System.NotSupportedException, but unfortunately my UI did not Update properly if I set the my ObservableCollectiony<MyType> from another thread.

The only thing that worked for me is presented by https://www.codeproject.com/Tips/1111432/Update-data-to-WPF-control-from-another-thread:

In ViewModel

private SynchronizationContext _syncContext = SynchronizationContext.Current;

private ObservableCollection<PackageModel> _packageModelList;
public ObservableCollection<PackageModel> PackageModelList
{
     get => _packageModelList;
     set
     {
         if (_packageModelList == value)
             return;
         packageModelList = value;
         RaisePropertyChanged("PackageModelList");
     }
}      

In other thread

 _syncContext.Send(x => { PackageModelList = OtherThreadPackageModels; },null);

Hope this helps others.

MaxWolodin
  • 129
  • 1
  • 7