Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF
Tip/Trick

Thread Safe Improvement for ObservableCollection

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
2 Jul 2012CPOL2 min read 61.6K   472   11   13
Why didn’t Microsoft provide this in the ObservableCollection?

Introduction

I was working on a WPF project where I had the ViewModel mirroring the Model displaying a hierarchical view of the data (like a tree view, but using XamDataGrid). I wanted to have an event in the Model that triggered when a record was added or removed to reduce coupling (probably when changes are made in the Model in the future). This was because I expected to be using the Model side with many different ViewModels, although at this point not sure since I am being given the requirements piece-meal. When I did this in a case where I had a BackgroundWorker, I started having problems with being on the wrong thread. Of course I expected that, and I immediately implemented a Dispatcher on the receiving class using code like the following:

C#
Dispatcher.CurrentDispatcher.Invoke(new Action<StagedBlotterOrderAggViewModel>(Add), adding);

This did not work so I attempted to use DispatcherPriority. Nothing looked right to me, but I tried several options and none worked.

I could go back to a design of trying to ensure that the record updates were done in the BackgroundWorkerCompleted handler, but that would complicate the code.

Of course I know that you can get the Dispatcher from the control, but that was not very elegant. Therefore I started to search the web, and found something interesting at “Have worker thread update ObservableCollection that is bound to a ListCollectionView” (http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx). I was not sure it would work, but I tried it, and it did. Of course I guess eventually I would have come up with inheriting from ObservableCollection<T> since I knew that I could get the Dispatcher from the control and then would not have had to do anything special in the ViewModel, but I am sure this is better than what I would have come up with. I did make a small number of improvements of adding the other constructors.

Here is my slightly improved version of this code that worked so well for me:

C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Threading;
 
namespace Custom.Collections
{
  public class ObservableCollectionEx<t> : ObservableCollection<t>
  {
    // Override the event so this class can access it
    public override event NotifyCollectionChangedEventHandler CollectionChanged;
 
    public ObservableCollectionEx(IEnumerable<t> collection) : base(collection) { }
    public ObservableCollectionEx(List<t> collection) : base(collection) { }
 
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
      // Be nice - use BlockReentrancy like MSDN said
      using (BlockReentrancy())
      {
        var eventHandler = CollectionChanged;
        if (eventHandler != null)
        {
          Delegate[] delegates = eventHandler.GetInvocationList();
          // Walk thru invocation list
          foreach (NotifyCollectionChangedEventHandler handler in delegates)
          {
            var dispatcherObject = handler.Target as DispatcherObject;
            // If the subscriber is a DispatcherObject and different thread
            if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
              // Invoke handler in the target dispatcher's thread
              dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, 
                            handler, this, e);
            else // Execute handler as is
              handler(this, e);
          }
        }
      }
    }
  }
}</t></t></t></t>

Now my big question is: Why didn’t Microsoft provide this in the original ObservableCollection?

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
BugA race condition can result in the UI displaying each item twice Pin
Dave Mackersie4-Nov-15 6:16
Dave Mackersie4-Nov-15 6:16 
AnswerRe: A race condition can result in the UI displaying each item twice Pin
Clifford Nelson4-Nov-15 8:54
Clifford Nelson4-Nov-15 8:54 
Questionperformance Pin
kadzbi25-May-14 23:24
kadzbi25-May-14 23:24 
QuestionChange made in .Net 4.5.1 (at least) Pin
BC3Tech16-May-14 7:32
BC3Tech16-May-14 7:32 
AnswerRe: Change made in .Net 4.5.1 (at least) Pin
Clifford Nelson4-Nov-15 8:54
Clifford Nelson4-Nov-15 8:54 
QuestionNot thread-safe Pin
Michael Bendtsen4-Dec-12 2:25
Michael Bendtsen4-Dec-12 2:25 
Thanks for you post. It worked to some extend.

You do know that this doesn't make the ObservableCollection thread-safe?
It makes it cross-thread "safe" or handles cross-thread operations.

This kind only handles things with the ICollectionView. Like the new feature in 4.5 does.
C#
BindingOperations.EnableCollectionSynchronization(this, m_Lock)

The new feature in 4.5 does not handles creating the ObservableCollection from another thread. It requires that the ObservableCollection is created on the Main thread.
Your post fixed that for me. Thanks.

I then have to consider if thread safety is important Smile | :)

Regards,
Michael
AnswerRe: Not thread-safe Pin
Clifford Nelson4-Dec-12 5:20
Clifford Nelson4-Dec-12 5:20 
QuestionNice try Pin
Jasper4C#3-Jul-12 2:54
Jasper4C#3-Jul-12 2:54 
AnswerRe: Nice try Pin
FatCatProgrammer3-Jul-12 4:11
FatCatProgrammer3-Jul-12 4:11 
AnswerRe: Nice try Pin
Clifford Nelson3-Jul-12 6:54
Clifford Nelson3-Jul-12 6:54 
AnswerRe: Nice try Pin
Clifford Nelson3-Jul-12 6:50
Clifford Nelson3-Jul-12 6:50 
QuestionReponse to your question Pin
Nicolas Dorier3-Jul-12 1:49
professionalNicolas Dorier3-Jul-12 1:49 
GeneralRe: Reponse to your question Pin
Clifford Nelson3-Jul-12 6:59
Clifford Nelson3-Jul-12 6:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.