All posts in Cross-thread

Welcome to the last part our series Making a Better ObservableCollection. If you missed Making a Better ObservableCollection Part 3 – Sorting you can get to it here. To wrap up using our extended ObservableCollection and Sorting, we are going to implement enhanced datagrid sorting by overriding the default sorting behavior with our new Sorting method.

Q: Why?
A: A fair question. It’s been found through multiple speed tests that using the default sort behavior of a DataGrid is actual rather slow on large data sets. Overriding this behavior with the method shown in Part 3 has been proven to greatly increase performance. More about that can be found in the original article which I based my research on here.

First, we need to set our UpdateSort method that will be called any time we sort our DataGrid with our extended ObservableCollection MyCollection:


/// <summary>
/// Updates the observable collection to the current sort context.
/// </summary>
public void UpdateSort(DataGridColumn sortColumn, ListSortDirection sortDirection)
{
    // As sort column needs to be present to sort.
    if (sortColumn == null) return;

    // Maintain sort direction
    sortColumn.SortDirection = sortDirection;

    // Sort member
    var sortMemberPath = sortColumn.SortMemberPath;

    // Sort
    this.MyCollection.Sort(sortMemberPath, sortDirection);
}

All we are doing here is setting the sort column and the sort direction. These are required for the Sort method to be executed.

Now, all we need to do is set the Sorted event of our DataGrid to ignore it’s own behavior and to use our new UpdateSort method instead:


/// <summary>
/// Handles sorting event for Data grid.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Controls.DataGridSortingEventArgs"/> 
/// instance containing the event data.</param>
public void DataGridSorting(object sender, DataGridSortingEventArgs e)
{
    // Prevent the built-in sort from sorting
    e.Handled = true;

    // Get current sort column
    var sortColumn = e.Column;
    var sortDirection =
        (SortColumn.SortDirection != ListSortDirection.Ascending) ?
        ListSortDirection.Ascending : ListSortDirection.Descending;

    // Sort
    // Call Sorted event on complete
    UpdateSort(sortColumn, sortDirection);
}

The key here is setting e.Handled = true to tell the Sorting event “It’s taken care of”, so that we can do whatever we want in it’s stead.

And just like that, we have overridden the default sorting behavior of our DataGrid to use our custom method to handle sorting an extended ObservableCollection.

I hope you enjoyed this 4-part series on extending an ObservableCollection.

Welcome to Part 3 of our series Making a Better ObservableCollection.

If you missed Making a Better ObservableCollection Part 2 – Cross Threading you can get to it here.

We started by showing you a few neat extensions in Part 1 and then followed it up with cross-threading in Part 2. Now that we have an understanding of how to extend and offload the work to another thread, let’s cover another very important feature: Sorting.

We start by creating a custom comparer as learned here:


/// <summary>
/// Customer Sort Comparer.
/// Original Source:
/// http://blogs.msdn.com/b/jgoldb/archive/2008/08/28/improving-microsoft-datagrid-ctp-sorting-performance-part-2.aspx
/// </summary>
/// <typeparam name="T"></typeparam>
internal class CustomSortComparer<T> : ICustomSortComparer<T>
{
    #region Members

    /// <summary>
    /// A two argument delegate for comparing two objects.
    /// </summary>
    /// <param name="arg1">The arg1.</param>
    /// <param name="arg2">The arg2.</param>
    /// <returns></returns>
    protected delegate int TwoArgDelegate(T arg1, T arg2);

    /// <summary>
    /// A two argument delegate instance.
    /// </summary>
    private TwoArgDelegate _myCompare;

    #endregion

    #region Methods

    /// <summary>
    /// Sorts the specified target collection.
    /// </summary>
    /// <param name="targetCollection">The target collection.</param>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="direction">The direction.</param>
    public void Sort(ObservableCollection<T> targetCollection, string propertyName, 
        ListSortDirection direction)
    {
        // Sort comparer
        var sortComparer = new InternalSorting(propertyName, direction);

        // Sort
        var sortedCollection = targetCollection.OrderBy(x => x, sortComparer).ToList();

        // Synch
        targetCollection.SynchCollection(sortedCollection, true);
    }

    /// <summary>
    /// Performs custom sorting operation.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="direction">The direction.</param>
    internal void CustomSort(string propertyName, ListSortDirection direction)
    {
        int dir = (direction == ListSortDirection.Ascending) ? 1 : -1;

        // Set a delegate to be called by IComparer.Compare
        _myCompare = (a, b) => ReflectionCompareTo(a, b, propertyName) * dir;
    }

    /// <summary>
    /// Custom compareTo function to compare 2 objects derived using Reflection.
    /// If an aliasProperty is provided, the sort is performed on that property
    /// instead.
    /// This is ideal for columns with data types that need to be sorted by another
    /// data type.
    /// i.e. Images that need value associations, or strings with numeric entries.
    /// </summary>
    /// <param name="a">A.</param>
    /// <param name="b">The b.</param>
    /// <param name="propertyName">Name of the property.</param>
    /// <returns></returns>
    private static int ReflectionCompareTo(object a, object b, String propertyName)
    {
        // Get property value for "a"
        PropertyInfo aPropInfo = a.GetType().GetProperty(propertyName);
        var aValue = aPropInfo.GetValue(a, null);
        if (aValue == null) return 0;

        // Get property value for "b"
        PropertyInfo bPropInfo = b.GetType().GetProperty(propertyName);
        var bValue = bPropInfo.GetValue(b, null);
        if (bValue == null) return 0;

        // CompareTo method
        MethodInfo compareToMethod =
            aPropInfo.PropertyType.GetMethod("CompareTo", new[] { aPropInfo.PropertyType });
        if (compareToMethod == null) return 0;

        // Get result
        var compareResult = compareToMethod.Invoke(aValue, new[] { bValue });
        return Convert.ToInt32(compareResult);
    }

    #endregion

    #region ICompare

    /// <summary>
    /// Compares two objects and returns a value indicating whether one is less 
    /// than, equal to, or greater than the other.
    /// </summary>
    /// <param name="x">The first object to compare.</param>
    /// <param name="y">The second object to compare.</param>
    /// <returns>
    /// Value
    /// Condition
    /// Less than zero
    /// <paramref name="x" /> is less than <paramref name="y" />.
    /// Zero
    /// <paramref name="x" /> equals <paramref name="y" />.
    /// Greater than zero
    /// <paramref name="x" /> is greater than <paramref name="y" />.
    /// </returns>
    public int Compare(T x, T y)
    {
        return _myCompare(x, y);
    }

    #endregion

    #region InternalSorting

    /// <summary>
    /// Custom IComparer class to perform custom sorting.
    /// </summary>
    private class InternalSorting : CustomSortComparer<T>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="InternalSorting"/> class.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="direction">The direction.</param>
        public InternalSorting(string propertyName, ListSortDirection direction)
        {
            CustomSort(propertyName, direction);
        }
    }

    #endregion
}

Q: So, what is going on with this comparer?
A: Here is a summary:

  • Sort is called against an ObservableCollection, a property name, and a sort direction.
  • A sort comparer is derived by calling an internal class which invokes the CustomSort method.
  • The CustomSort method assesses the sort direction as an integer and then uses reflection to compare each set of values (ReflectionCompareTo).
  • A sorted collection is created against the target collection with the sort comparer applied.
  • The target collection is synched against the sorted collection.

Next, we augment our ObservableCollectionEx class:


/// <summary>
/// Extends the ObservableCollection object:
/// 1. Allows cross-thread updating to offload UI operations.
/// 2. Allows full sorting capabilities without affecting the UI thread.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableCollectionEx<T> : ObservableCollection<T>, IObservableCollectionEx<T>
{
    #region Members

    private readonly ICustomSortComparer<T> _sortComparer;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the 
    /// <see cref="ObservableCollectionEx{T}"/> class.
    /// </summary>
    public ObservableCollectionEx()
    {
        _sortComparer = new CustomSortComparer<T>();
    }

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="ObservableCollectionEx{T}" /> class.
    /// </summary>
    /// <param name="collection">The collection.</param>
    public ObservableCollectionEx(IEnumerable<T> collection) : this()
    {
        this.AddRange(collection);
    }

    #endregion

    #region Methods

    /// <summary>
    /// Sorts the observable collection by the property and sort direction.
    /// </summary>
    /// <param name="propertyName">The property within the ObservableCollectionExtender object
    /// to sort by.</param>
    /// <param name="direction">The desired sort direction.</param>
    public void Sort(string propertyName, ListSortDirection direction)
    {
        if (!this.Any()) return;
        _sortComparer.Sort(this, propertyName, direction);
    }

    /// <summary>
    /// Sorts the specified expression.
    /// </summary>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="expression">The expression.</param>
    /// <param name="direction">The direction.</param>
    public void Sort<TProperty>(Expression<Func<T, TProperty>> expression, 
    ListSortDirection direction)
    {
        if (!this.Any()) return;
        Sort(expression.GetPropertyName(), direction);
    }

    #endregion

    #region Events

    /// <summary>
    /// Source: New Things I Learned
    /// Title: 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
    /// Note: Improved for clarity and the following of proper coding standards.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)  
    {
        // Use BlockReentrancy
        using (BlockReentrancy())
        {
            var eventHandler = CollectionChanged;
            if (eventHandler == null) return;

            // Only proceed if handler exists.
            Delegate[] delegates = eventHandler.GetInvocationList();

            // Walk through invocation list.
            foreach (var @delegate in delegates)
            {
                var handler = (NotifyCollectionChangedEventHandler)@delegate;
                var currentDispatcher = handler.Target as DispatcherObject;

                // If the subscriber is a DispatcherObject and different thread.
                if ((currentDispatcher != null) &amp;&amp; (!currentDispatcher.CheckAccess()))
                {
                    // Invoke handler in the target dispatcher's thread.
                    currentDispatcher.Dispatcher.Invoke(
                        DispatcherPriority.DataBind, handler, this, e);
                }

                else
                {
                    // Execute as-is
                    handler(this, e);
                }
            }
        }
    }
}

/// <summary>
/// Overridden NotifyCollectionChangedEventHandler event.
/// </summary>
public override event NotifyCollectionChangedEventHandler CollectionChanged;

#endregion

What’s new is our 2 Sort methods. One uses a property name and the other an expression to strongly define the property. Both accomplish calling the sort comparer we just built earlier.

Q: So, why do all this? What was the point?
A: Custom sort comparers will give us the ability to increase the sorting performance of our ObservableCollections when bound to controls. It will also allow us to perform this work in an async thread instead of having to create a CollectionViewSource on the UI thread which will negatively impact the user experience during updates.

Next time, we will look at applying this to a DataGrid to greatly improve sort performance and reliability.

Welcome to Part 2 of our series Making a Better ObservableCollection. If you missed Making a Better ObservableCollection Part 1 – Extensions you can get to it here.

In this next section I am going to share a version of my ObservableCollectionEx that allows cross-threading. The idea here is to have an ObservableCollection which you can update from an Async thread so as not to impact the owning thread. This is especially useful in WPF when you don’t wish to block the UI thread while performing collection updates.

Let’s see the code:


/// <summary>
/// Initializes a new instance of the 
/// <see cref="ObservableCollectionEx{T}"/> class.
/// </summary> 
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="ObservableCollectionEx{T}" /> class.
    /// </summary>
    public ObservableCollectionEx()
    {
    }

    ///
    /// Initializes a new instance of the
    ///  class.
    ///
    ///The collection.
    public ObservableCollectionEx(IEnumerable<T> collection) : this()
    {
        this.AddRange(collection);
    }

    #endregion

    #region Events

    /// <summary>
    /// Source: New Things I Learned
    /// Title: 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
    /// Note: Improved for clarity and the following of proper coding standards.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        // Use BlockReentrancy
        using (BlockReentrancy())
        {
            var eventHandler = CollectionChanged;
            if (eventHandler == null) return;

            // Only proceed if handler exists.
            Delegate[] delegates = eventHandler.GetInvocationList();

            // Walk through invocation list.
            foreach (var @delegate in delegates)
            {
                var handler = (NotifyCollectionChangedEventHandler)@delegate;
                var currentDispatcher = handler.Target as DispatcherObject;

                // If the subscriber is a DispatcherObject and different thread.
                if ((currentDispatcher != null) &amp;&amp; (!currentDispatcher.CheckAccess()))
                {
                    // Invoke handler in the target dispatcher's thread.
                    currentDispatcher.Dispatcher.Invoke(
                        DispatcherPriority.DataBind, handler, this, e);
                }

                else
                {
                    // Execute as-is
                    handler(this, e);
                }
            }
        }
    }

    /// <summary>
    /// Overridden NotifyCollectionChangedEventHandler event.
    /// </summary>
    public override event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion
}

The constructors are pretty straight forward. We want to have an empty constructor and one that allows an immediate “AddRange” of an IEnumerable just like List and ObservableCollection allow, but that is not really the point of this post.

The main feature here is a slightly reformatted version of a wonderful post from a blog called New Things I Learned which covers cross-thread access with an ObservableCollection.

So, what does this code do?

  1. First we use BlockReentracy to prevent changes to the collection while we are evaluating it.
  2. Next, we get the Invocation List from the CollectionChanged event handler. This is the list of delegates subscribing to the event.
  3. Evaluate each delegate by retrieving it’s Target and casting it to a DispatcherObject.
  4. Make sure the DispatcherObject is valid and that the current thread has access.
  5. Invoke the delegate in the DispatcherObject’s thread.

It’s not the simplest code to follow when you aren’t overly familiar with working with event delegates but it gets the job done quite well.

Next time, we will talk about using a custom SortComparer to improve DataGrid performance.

Until next time.