WPF 4.5 – Part 10 : Live shaping (live filtering, grouping and sorting of collections)

, , 7 Comments

For each item list used in a WPF application, a collection view is in fact created and used by the controls. This view enables navigation, filtering, grouping and sorting from the XAML or the code. It is a very powerful feature that is offered to the developers since a long time in WPF.

In WPF 4.5 as in WPF 4.0 the grouping, sorting and filtering operations are done when the item is added to the collection or when the Refresh method is called. The drawback is that if an item property value involved in one of these operations is updated, then the sorting/grouping/filtering will not be done again.

WPF 4.5 introduce a feature called live shaping which shapes the collection view in live. Let’s dig a little more this feature.

This post is the tenth part of a serie on the WPF 4.5 new features.

Basic behavior

When you use a collection in WPF, it’s in fact its default view which is used by the controls. You should read Bea Stollnitz’s post on this feature if you want more information.

You can retrieve the default view on the collection by using the CollectionViewSource static method named GetDefaultView:
[csharp] var collectionView
= CollectionViewSource.GetDefaultView(Persons) as ICollectionView;[/csharp]

The ICollectionView interface exposes methods which enable grouping and sorting described via the GroupDescriptions and SortDescriptions collection respectively. The grouping can then be used in ItemsControl by setting the GroupStyle property. In the snippet below, the persons are sorted and grouped by age:
[csharp]//Group by age
collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Age"));

//sort by age
collectionView.SortDescriptions.Add(new SortDescription("Age",
ListSortDirection.Ascending));
[/csharp]

Also, a predicate can be defined to tells if an item is exposed or not by the view on the collection. In this example we do not expose children(under 18):
[csharp]
private void ApplyFilter(ICollectionView collectionView)
{
//Apply the filter
collectionView.Filter = IsPersonAccepted;
}

//Children are not accepted
public bool IsPersonAccepted(object item)
{
Person p = item as Person;
return (p != null) && p.Age > 18;
}
[/csharp]

The items are filtered, sorted and grouped on two occasions:

  • When they are added to the collection which is the source of the view.
  • When you call the Refresh method on a view.

This is fine but it does not cover every possible scenario. For example, if an item update the value of the property on which one of this operation is done, no refreshing is done.

To illustrate this, I created a little demo which groups and sort a collection of persons by age and removes the one under 18. It can be found on my Dropbox folder after registration. A button, changes randomly the age of the persons. An another adds one year to each of the persons of the collection. After several click on those buttons, it ends up with a list displaying strange things: the groups are no more representative and the persons are no more sorted. More over, there is children(under 18) who are displayed and some who are not :
Live sorting is not enabled example

This can be a wanted feature as you may want the collection display to be updated in live. If so, the live shaping is here for you in WPF 4.5 !

Live shaping

This new feature is an opt-in one. You can activate live sorting, live grouping and live filtering separately and by default each is disabled.

The ICollectionView interface is still the same and nothing more is exposed. However you have to use a new interface named ICollectionViewLiveShaping.

There is three properties relative to each operation (grouping, sorting, filtering):

  • A boolean CanChangeLiveXXX telling if the live shaping for the operation XXX can be activated. It is read-only. BindingListCollectionView and ListCollectionView always returns true at this time.
  • A null-able boolean IsLiveXXX telling if the live shaping for the operation XXX is activated.
  • A collection of strings LiveXXXProperties containing the names of the property on the view’s item regarding to the operation.

The most important one is maybe the last. When you define a sorting, a grouping or a filter, some property of the contained items are involved. By adding a property name in the LiveXXXProperties collection
you tell the framework: on each change of the property with this name, you have to refresh the operation XXX
. As you can have guessed, it works using the INotifyPropertyChanged interface. The collection view register to each of the item added to the collection and listen for changes at the aimed properties. Simple as that !

The process to activate one operation is this one:

  1. Be sure that the aimed item implement INotifyPropertyChanged;
  2. Retrieve the ICollectionViewLiveShaping;
  3. Check if the operation XXX can be activated;
  4. Add the property involved in the XXX operation in the LiveXXXProperties;
  5. Activate live XXX operation via the IsLiveXXX property.

I created some helpers for the above demo application which activate each of them.

Live sorting

[csharp]
//In a method before
ActiveLiveSorting(collectionView, new List<string>("Age");
// …

private void ActiveLiveSorting(ICollectionView collectionView,
IList<string> involvedProperties)
{
var collectionViewLiveShaping = collectionView as ICollectionViewLiveShaping;
if (collectionViewLiveShaping == null) return;
if (collectionViewLiveShaping.CanChangeLiveSorting)
{
foreach(string propName in involvedProperties)
collectionViewLiveShaping.LiveSortingProperties.Add(propName);
collectionViewLiveShaping.IsLiveSorting = true;
}
}

[/csharp]

Live grouping

[csharp]
//In a method before
ActiveLiveGrouping(collectionView, new List<string>("Age");
// …

private void ActiveLiveGrouping(ICollectionView collectionView,
IList<string> involvedProperties)
{
var collectionViewLiveShaping = collectionView as ICollectionViewLiveShaping;
if (collectionViewLiveShaping == null) return;
if (collectionViewLiveShaping.CanChangeLiveGrouping)
{
foreach(string propName in involvedProperties)
collectionViewLiveShaping.LiveGroupingProperties.Add(propName);
collectionViewLiveShaping.IsLiveGrouping = true;
}
}

[/csharp]

Live filtering

[csharp]
//In a method before
ActiveLiveFiltering(collectionView, new List<string>("Age");
// …

private void ActiveLiveFiltering(ICollectionView collectionView,
IList<string> involvedProperties)
{
var collectionViewLiveShaping = collectionView as ICollectionViewLiveShaping;
if (collectionViewLiveShaping == null) return;
if (collectionViewLiveShaping.CanChangeLiveFiltering)
{
foreach(string propName in involvedProperties)
collectionViewLiveShaping.LiveFilteringProperties.Add(propName);
collectionViewLiveShaping.IsLiveFiltering = true;
}
}

[/csharp]

Then you can play as much as you want with the buttons, the group, sorting and filtering is performed well.

Side notes

I am very excited about this feature but it is a very expensive one so be careful to activate it only if needed and only the one you actually need. It may be a good idea too to de-activate it when it’s no more needed.

Finally, you may find interesting that the ItemsCollection class, which is used to hold the list of items of an ItemControls implement the ICollectionViewLiveShaping interface too.

Regards,

 

7 Responses

  1. Thomas Fröhle

    12/05/2012 19 h 39 min

    Hey Jonathan,
    great post! I can't access the files in your dropbox-folder. It seems to be empty. Is there another way to get to your sourcecode?
    Greetings
    Thomas

Comments are closed.