DispatchingObservableCollection – Thread save ObservableCollection

If you ever worked with thread in you WPF application you probably have had the problem that you changed some values in a thread (different from the GUI-Thread) and then you see an Exception like thisone:

This CollectionView-Type does not allow any changes if they are not executed from the Dispatcher-Thread.

I tried to translate it in English.

In order to avoid that I coded a ObservableCollection which does the dispatching by itself. Which means that it invokes if necessary such that every change (add, insert, clear, move or whatever) is executed in the right thread. Not that the right thread isn’t always the GUI-Thread. The right thread is always the thread which created the object (owner).
If you use this (modified) ObservableCollection you don’t have to remember if you are in the right (owner) thread.

    /// <summary>
    /// This class is an observablecollection which invokes automatically.
    /// This means that any change will be done in the right thread.
    /// </summary>
    /// <typeparam name="T">The type of the elements</typeparam>
    public class DispatchingObservableCollection<T> : ObservableCollection<T>
    {
        /// <summary>
        /// The default constructor of the ObservableCollection
        /// </summary>
        public DispatchingObservableCollection()
        {
            //Assign the current Dispatcher (owner of the collection)
            _currentDispatcher = Dispatcher.CurrentDispatcher;
        }

        private readonly Dispatcher _currentDispatcher;

        /// <summary>
        /// Executes this action in the right thread
        /// </summary>
        ///<param name="action">The action which should be executed</param>
        private void DoDispatchedAction(Action action)
        {
            if (_currentDispatcher.CheckAccess())
                action.Invoke();
            else
                _currentDispatcher.Invoke(DispatcherPriority.DataBind, action);
        }

        /// <summary>
        /// Clears all items
        /// </summary>
        protected override void ClearItems()
        {
            DoDispatchedAction(() => base.ClearItems());
        }

        /// <summary>
        /// Inserts a item at the specified index
        /// </summary>
        ///<param name="index">The index where the item should be inserted</param>
        ///<param name="item">The item which should be inserted</param>
        protected override void InsertItem(int index, T item)
        {
            DoDispatchedAction(() => base.InsertItem(index, item));
        }

        /// <summary>
        /// Moves an item from one index to another
        /// </summary>
        ///<param name="oldIndex">The index of the item which should be moved</param>
        ///<param name="newIndex">The index where the item should be moved</param>
        protected override void MoveItem(int oldIndex, int newIndex)
        {
            DoDispatchedAction(() => base.MoveItem(oldIndex, newIndex));
        }

        /// <summary>
        /// Removes the item at the specified index
        /// </summary>
        ///<param name="index">The index of the item which should be removed</param>
        protected override void RemoveItem(int index)
        {
            DoDispatchedAction(() => base.RemoveItem(index));
        }

        /// <summary>
        /// Sets the item at the specified index
        /// </summary>
        ///<param name="index">The index which should be set</param>
        ///<param name="item">The new item</param>
        protected override void SetItem(int index, T item)
        {
            DoDispatchedAction(() => base.SetItem(index, item));
        }

        /// <summary>
        /// Fires the CollectionChanged Event
        /// </summary>
        ///<param name="e">The additional arguments of the event</param>
        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            DoDispatchedAction(() => base.OnCollectionChanged(e));
        }

        /// <summary>
        /// Fires the PropertyChanged Event
        /// </summary>
        ///<param name="e">The additional arguments of the event</param>
        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            DoDispatchedAction(() => base.OnPropertyChanged(e));
        }
    }

Note that the version above yields warning CS1911 which could cause problems if you have to run your code in partial-trust.
In order to avoid this warning I had to expand the code in extra / helper methods.

This version of the dispatching observablecollection compiles without this warning.

using System;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System.Collections.Specialized;
using System.ComponentModel;

namespace MyObservableCollection
{
    /// <summary>  
    /// This class is an observablecollection which invokes automatically.  
    /// This means that any change will be done in the right thread.  
    /// </summary>  
    /// <typeparam name="T">The type of the elements</typeparam>  
    public class DispatchingObservableCollection<T> : ObservableCollection<T>
    {
        /// <summary>  
        /// The default constructor of the ObservableCollection  
        /// </summary>  
        public DispatchingObservableCollection()
        {
            //Assign the current Dispatcher (owner of the collection)  
            _currentDispatcher = Dispatcher.CurrentDispatcher;
        }

        private readonly Dispatcher _currentDispatcher;

        /// <summary>  
        /// Executes this action in the right thread  
        /// </summary>  
        ///<param name="action">The action which should be executed</param>  
        private void DoDispatchedAction(Action action)
        {
            if (_currentDispatcher.CheckAccess())
                action.Invoke();
            else
                _currentDispatcher.Invoke(DispatcherPriority.DataBind, action);
        }

        /// <summary>  
        /// Clears all items  
        /// </summary>  
        protected override void ClearItems()
        {
            DoDispatchedAction(BaseClearItems);
        }

        private void BaseClearItems()
        {
            base.ClearItems();
        }

        /// <summary>  
        /// Inserts a item at the specified index  
        /// </summary>  
        ///<param name="index">The index where the item should be inserted</param>  
        ///<param name="item">The item which should be inserted</param>  
        protected override void InsertItem(int index, T item)
        {
            DoDispatchedAction(() => BaseInsertItem(index, item));
        }

        private void BaseInsertItem(int index, T item)
        {
            base.InsertItem(index, item);
        }

        /// <summary>  
        /// Moves an item from one index to another  
        /// </summary>  
        ///<param name="oldIndex">The index of the item which should be moved</param>  
        ///<param name="newIndex">The index where the item should be moved</param>  
        protected override void MoveItem(int oldIndex, int newIndex)
        {
            DoDispatchedAction(() => BaseMoveItem(oldIndex, newIndex));
        }

        private void BaseMoveItem(int oldIndex, int newIndex)
        {
            base.MoveItem(oldIndex, newIndex);
        }

        /// <summary>  
        /// Removes the item at the specified index  
        /// </summary>  
        ///<param name="index">The index of the item which should be removed</param>  
        protected override void RemoveItem(int index)
        {
            DoDispatchedAction(() => BaseRemoveItem(index));
        }

        private void BaseRemoveItem(int index)
        {
            base.RemoveItem(index);
        }

        /// <summary>  
        /// Sets the item at the specified index  
        /// </summary>  
        ///<param name="index">The index which should be set</param>  
        ///<param name="item">The new item</param>  
        protected override void SetItem(int index, T item)
        {
            DoDispatchedAction(() => BaseSetItem(index, item));
        }

        private void BaseSetItem(int index, T item)
        {
            base.SetItem(index, item);
        }
        
        /// <summary>  
        /// Fires the CollectionChanged Event  
        /// </summary>  
        ///<param name="e">The additional arguments of the event</param>  
        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            DoDispatchedAction(() => BaseOnCollectionChanged(e));
        }

        private void BaseOnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnCollectionChanged(e);
        }

        /// <summary>  
        /// Fires the PropertyChanged Event  
        /// </summary>  
        ///<param name="e">The additional arguments of the event</param>  
        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            DoDispatchedAction(() => BaseOnPropertyChanged(e));
        }

        private void BaseOnPropertyChanged(PropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
        }
    }
}
Advertisements
    • David
    • August 19th, 2009

    Nice, clean implementation. The use of Action here really cleans up what is otherwise really cluttered, as it is on another blog I was just reading about the problem space. Unfortunately the approach here yields warning CS1911 which could be suppressed for those who don’t need to be able to run in partial-trust, or easily expanding on the code to use helper methods solves the problem properly (just doesn’t look quite as pretty since there’d be about twice as much code) (http://msdn.microsoft.com/en-us/library/ms228459.aspx)

    Nice blog, I think I’ll track it 🙂

    ~ David

      • michlG
      • August 19th, 2009

      Hello David,

      thank you for your comment.
      I have also seen other implementations which are much longer and more complicated.
      Therefore I decided to create this implementation 🙂
      If I have enough time I will blog also the version with the helper methods in order to have a code which can be run in partial-trust.
      Thank you

      Michael

  1. That’s a nice piece of code – thanks for sharing, @michlG!

    • dct
    • February 17th, 2012

    Nice work!

    • james igoe
    • July 18th, 2012

    Only recently started working with WPF and MVVM, and this saved me untold hours. Beattiful, much appreciated.

    • Monique Hawkins
    • May 10th, 2013

    I found I had to make the owner always the UI thread (ie set _currentDispatcher = System.Windows.Application.Current.Dispatcher), because if I had a non-UI thread creating the collection, then the UI thread accessing it, the UI thread would block itself on the “Invoke” in DoDispatchedAction.

    • Anthony
    • April 17th, 2014

    @Monique : To handle these type of edge cases try this collection which takes care of this problem as well as other multi-threaded problems that will inevitably crop up with other approaches : http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: