.netwpfsorting

What causes a WPF ListCollectionView that uses custom sorting to re-sort its items?


Consider this code (type names genericised for the purposes of example):

// Bound to ListBox.ItemsSource
_items = new ObservableCollection<Item>();

// ...Items are added here ...

// Specify custom IComparer for this collection view
_itemsView = CollectionViewSource.GetDefaultView(_items)
((ListCollectionView)_itemsView).CustomSort = new ItemComparer();

When I set CustomSort, the collection is sorted as I expect.

However I require the data to re-sort itself at runtime in response to the changing of the properties on Item. The Item class derives from INotifyPropertyChanged and I know that the property fires correctly as my data template updates the values on screen, only the sorting logic is not being called.

I have also tried raising INotifyPropertyChanged.PropertyChanged passing an empty string, to see if a generic notification would cause the sorting to be initiated. No bananas.

EDIT In response to Kent's suggestion I thought I'd point out that sorting the items using this has the same result, namely that the collection sorts once but does not re-sort as the data changes:

_itemsView.SortDescriptions.Add(
    new SortDescription("PropertyName", ListSortDirection.Ascending));

Solution

  • I found this article by Dr. WPF which starts out with an answer to my question, then moves on to discuss the performance impact of calling Refresh. Some key excerpts:

    Unfortunately, the Refresh() method results in a complete regeneration of the view. When a Refresh() occurs within the view, it raises a CollectionChanged notification and supplies the Action as "Reset". The ItemContainerGenerator for the ListBox receives this notification and responds by discarding all the existing visuals for the items. It then completely regenerates new item containers and visuals.

    He then describes a hacky workaround that improves performance. Rather than calling Refresh, remove, change then re-add the item.

    I would have thought it possible that the list view could track an item that changes and know to re-position that item alone within the view.

    A new approach was introduced in .NET 3.5 SP1 involving the interface IEditableObject that provides transactional editing via data bindings to the template with methods BeginEdit(), CancelEdit(), and EndEdit(). Read the article for more information.

    EDIT As user346528 points out, IEditableObject was not in fact new in 3.5SP1. It actually looks like it's been in the framework since 1.0.