Posts Tagged ‘ Invoke ’

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

Advanced INotifyPropertyChanged with an automatic dispatcher

In WPF it is really important that you do not fire the PropertyChanged and CollectionChanged Events from a different thread then its creator.
Usually you have to check each time if you do not have to Invoke.

I wrote an abstract class which implements the INotifyPropertyChanged interface and which always fires the event from the right thread.
So it is no problem if you change the value of a property from another thread because the event is fired from the right thread.

Another problem of the PropertyChangedEvent is the PropertyChangedEventArgs where you have to specify the name of the property which change.
If you missspell the name of the property it will not update the databinding. Such problems are really difficult to find.
In order to reduce the risk of writing the wrong name I just added a few lines of code which check if there is a property with the given name. If not it throws an exception.
You could improve this class using the extension method from this website: http://blog.decarufel.net/2009/07/how-to-use-inotifypropertychanged-type.html

This is my DispatchingPropertyChangedProperty class which implements INotifyPropertyChanged and add the extra funcionality explained above.

	public abstract class DispatchingPropertyChangedProperty : INotifyPropertyChanged
	{
		protected DispatchingPropertyChangedProperty()
		{
			_currentDispatcher = Dispatcher.CurrentDispatcher;
		}

		private Dispatcher _currentDispatcher;
    
		private delegate void NotifyPropertyChangedDeleagte(object sender, string name);

		/// 
		/// This method executes the PropertyChanged-Event with the name of the property
		/// 
		/// The sender of the event
		/// The string of the property whichs value changed
		public void NotifyPropertyChanged(object sender, string name)
		{
			if(sender == null || name == string.Empty)
				throw new NullReferenceException("You have to specify a sender and the name of the property which changed");
			
			//Check if the given name is valid
			if (sender.GetType().GetProperty(name) != null)
			{
				if (_currentDispatcher.CheckAccess())
				{
					//We are in the correct thread
					//Execute the PropertyChanged-Event
					var handler = PropertyChanged;
					if (handler != null)
						handler(sender, new PropertyChangedEventArgs(name));
				}
				else
					_currentDispatcher.Invoke(DispatcherPriority.DataBind, new NotifyPropertyChangedDeleagte(NotifyPropertyChanged), sender, name);
			}
			else
				throw new Exception("The given name does have a corresponding property");
		}

		public event PropertyChangedEventHandler PropertyChanged;
	}

This is a class (Person) which uses the DispatchingPropertyChangedProperty class

public class Person : DispatchingPropertyChangedProperty
	{
		private string _firstname;
		private string _lastname;
		private int _age;
		private bool _isMale;

		public string Firstname
		{
			get{return _firstname;}
			set
			{
				_firstname = value;
				NotifyPropertyChanged(this,"Firstname");
			}
		}

		public string Lastname
		{
			get { return _lastname; }
			set
			{
				_lastname = value;
				NotifyPropertyChanged(this,"Lastname");
			}
		}

		public int Age
		{
			get { return _age; }
			set
			{
				_age = value;
				NotifyPropertyChanged(this,"Age");
			}
		}

		public bool IsMale
		{
			get { return _isMale; }
			set
			{
				_isMale = value;
				NotifyPropertyChanged(this,"IsMale");
			}
		}
	}