Wednesday, August 28, 2013

Thread Safe ObservableCollection in WPF

The following is an improvement on the implementation found by Jonathan. Firstly it runs each event handler on the dispatcher associated with it rather than assuming that they are all on the same (UI) dispatcher. Secondly it uses BeginInvoke to allow processing to continue while we wait for the dispatcher to become available. This makes the solution much faster in situations where the background thread is doing lots of updates with processing between each one. Perhaps more importantly it overcomes problems caused by blocking while waiting for the Invoke (deadlocks can occur for example when using WCF with ConcurrencyMode.Single).

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Windows.Threading;

namespace ThreadSafeApplication.Helper
{
  public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
  {
    public override event NotifyCollectionChangedEventHandler CollectionChanged;
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
      NotifyCollectionChangedEventHandler collectionChanged = this.CollectionChanged;
      if (collectionChanged != null)
        foreach (NotifyCollectionChangedEventHandler handler in collectionChanged.GetInvocationList())
        {
          DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
          if (dispatcherObject != null)
          {
            Dispatcher dispatcher = dispatcherObject.Dispatcher;
            if (dispatcher != null && !dispatcher.CheckAccess())
            {
              dispatcher.BeginInvoke(
                  (Action)(() => handler.Invoke(this,
                      new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
                  DispatcherPriority.DataBind);
              continue;
            }
          }
          handler.Invoke(this, e);
        }
    }
  }
}


Reference: http://stackoverflow.com/questions/2137769/where-do-i-get-a-thread-safe-collectionview

5 comments:

  1. how do you use this? Do you create an observablelist in your model or viewmodel? if it is in the model, how do get get hold of an instance of dispatcher? if it is in the viewmodel, how do you wrap an observablelist around the original list/collection?

    ReplyDelete
  2. You don't have to do much with this except use it in place of ObservableCollection list. If you have different threads to update a observable collection then this would be the best way to have a thread safe observable collection in your code.

    ReplyDelete
  3. Hy, thank you for the great soluten. How do you update the list because I tried it but I have some issues. I used the Task.Factory.StartNew(().. task to update the list. Would be great if you have a solution for my problem because you already used it succesfully. I opened a thread here bacause I didn't get it worked.

    http://stackoverflow.com/questions/22513483/update-observablecollection-in-list-box-in-thread?noredirect=1#comment34255569_22513483

    Best regards

    ReplyDelete
  4. You need to maintain current dispatcher thread for the same. You must update collection in current dispatcher thread only. One way to do it is to use BiginInvoke() method of dispatcher class.

    For example: We have a scenario where we popup an error window if we have an error. We need to close an Error window if error count is zero. Now if we are handling events and message in another thread (not on UI thread) then we need to save the UI thread dispatcher object and need to use it to update collection.

    if (ErrorNotifications.Count == 0)
    _currentDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(CloseErrorNotificationWindow), _errWindow);

    Here CloseErrorNotificationWindow is method with parameter _errWindow.

    private void CloseErrorNotificationWindow(ErrorNotificationWindow _errWindow)
    {
    if (_errWindow == null)
    return;

    if (_errWindow.IsActive)
    _errWindow.Close();
    }

    Hope this will helpful.

    ReplyDelete
  5. Are you tired of seeking loans and Mortgages,have you been turned down constantly By your banks and other financial institutions,We offer any form of loan to individuals and corporate bodies at low interest rate.If you are interested in taking a loan,feel free to contact us today,we promise to offer you the best services ever.Just give us a try,because a trial will convince you.What are your Financial needs?Do you need a business loan?Do you need a personal loan?Do you want to buy a car?Do you want to refinance?Do you need a mortgage loan?Do you need a huge capital to start off your business proposal or expansion? Have you lost hope and you think there is no way out, and your financial burdens still persists? Contact us (gaincreditloan1@gmail.com)

    Your Name:...............
    Your Country:...............
    Your Occupation:...............
    Loan Amount Needed:...............
    Loan Duration...............
    Monthly Income:...............
    Your Telephone Number:.....................
    Business Plan/Use Of Your Loan:...............
    Contact Us At : gaincreditloan1@gmail.com
    Phone number :+44-75967-81743 (WhatsApp Only)

    ReplyDelete