Tris et filtres sur des ObservableCollection<T>

Utiliser des ObservableCollection<T> est une pratique fondamentale lors du développement d’UI. Outre le fait qu’elles permettent des mises à jour dynamique de l’UI, elles peuvent également être triées et filtrées par le biais des CollectionViews.

Ce post décrit un pattern réutilisable permettant de simplifier le code liant une ObservableCollection à une ou plusieurs ListCollectionViews, avec un projet exemple.

image

 Le principe est le suivant :

1. Définir une propriété get/set exposant l’ObservableCollection source, et autant de propriétés get qu’il y aura de vues de la source.

ObservableCollection<Personne> _sampleData; // qui est une ObservableCollection<Personne>

public PersonneCollection SampleData
{
    get { return _sampleData; }

    set
    {
        _sampleData = value;
        …
    }
}

ListCollectionView _cvSampleDataTriAge, _cvSampleDataFiltreAge;
public CollectionView CVSampleDataTriAge { get { return _cvSampleDataTriAge; } }
public CollectionView CVSampleDataFiltreAge { get { return _cvSampleDataFiltreAge; } }

 

2. Lorsque l’ObservableCollection est settée :

a. Instancier autant de ListCollectionViews qu’il y aura de vues différentes

b. Sur chacune d’elles définir si nécessaire:

                                                                          i.  la ou les SortDescriptions pour les tris

                                                                         ii. le prédicat de filtre

c. Déclencher PropertyChanged sur la propriété source et toutes les vues

set

{

    _sampleData = value;

    // On instancie les vues et leurs descriptions de tri/filtre
    _cvSampleDataTriAge = new ListCollectionView(_sampleData);
    _cvSampleDataTriAge.SortDescriptions.Add(new SortDescription("Age",
ListSortDirection.Ascending));
    _cvSampleDataFiltreAge = new ListCollectionView(_sampleData);
    _cvSampleDataFiltreAge.Filter += new Predicate<object>(FiltreAge);

    // On déclenche une notification pour les propriétés
    OnNotifyPropertyChanged("SampleData");
    OnNotifyPropertyChanged("CVSampleDataTriAge");
    OnNotifyPropertyChanged("CVSampleDataFiltreAge");  

    // Synchronisation du CurrentItem (optionnel)
    var cv = CollectionViewSource.GetDefaultView(_sampleData);
    cv.CurrentChanged += new EventHandler(delegate
        {
            _cvSampleDataTriAge.MoveCurrentTo(cv.CurrentItem);
            _cvSampleDataFiltreAge.MoveCurrentTo(cv.CurrentItem);
        });
}

3. Dans le XAML, se binder aux ListCollectionViews comme à n’importe quelle autre propriété

<ListBox ItemsSource="{Binding Path=CVSampleDataTriAge}"/>

 

Les vues seront automatiquement mises à jour en fonction de la composition (cf. NotifyCollectionChangedAction) de la source. Le changement de valeur d’une propriété d’un élément de la collection source ne déclenche pas de mise à jour des vues. Cette dernière problématique est traitée par le biais de l’ObservableNotifiableCollection, déjà expliqué dans un précédent post, sera décrite avec plus de détails dans un prochain message.

Détails de fonctionnement

Les CollectionViews permettent d’avoir une séparation entre la représentation de la collection utilisée pour l’affichage et la « vraie » collection source. Cela permet par exemple de filtrer une ObservableCollection source sans avoir à maintenir manuellement une synchronisation avec une collection intermédiaire.

image

CollectionViewDemo.zip