Binding de SelectedItems sur ListBox en WPF et Silverlight

Selection simple

La sélection d’un item dans une ListBox est très facile à mettre en place puisqu’il suffit d’effectuer un Binding en mode TwoWay sur la propriété

SelectedItem (ici dans une propriété SelectedString) :

<ListBox ItemsSource="{Binding Strings}" SelectedItem="{Binding SelectedString, Mode=TwoWay}" />
Selection multiple

La selection multiple peut servir dans le cas ou vous developpez une application à la Outlook ou Windows Live Mail (pour la selection de mail) ou encore Windows Explorer ( pour la selection de fichier).

Pour effectuer une sélection multiple d’items (avec le bouton Shift et Control) avec Binding, c’est, en revanche, plus compliqué que pour une selection simple.

Tout d’abord on affecte la propriété SelectedMode de la ListBox à Extended afin de prendre en compte la selection multiple.
Bien qu’il existe une propriété SelectedItems (noté le s, à ne pas confondre avec SelectedItem) celle ci est en lecture seule.
On ne pourra donc pas l’utiliser pour la Binder. On devra donc passer par l’évenement SelectedChanged. Pas très agréable.

Un behavior de sélection

Pour pallier à ce désagrément j’ai mis en place un Behavior que j’ai appelé SelectedItems (restons simple :) )

	<ListBox SelectionMode="Extended" ItemsSource="{Binding Strings}"local:SelectedItemsBehavior.SelectedItems="{Binding SelectedStrings,Mode=TwoWay}" 

/>

Le code du Behavior est très court. On commence par definir une Attached DependencyProperty via le snippet propa que l’on nomme SelectedItems

    public static class SelectedItemsBehavior
    {


        public static IList GetSelectedItems(DependencyObject obj)
        {
            return (IList)obj.GetValue(SelectedItemsProperty);
        }

        public static void SetSelectedItems(DependencyObject obj, IList value)
        {
            obj.SetValue(SelectedItemsProperty, value);
        }

        // Using a DependencyProperty as the backing store for SelectedItems.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedItemsProperty =
            DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(SelectedItemsBehavior), new PropertyMetadata(null, 

OnSelectedItemsChanged));

	...

    }

le PropertyMetadata (en Silverlight, ou UIPropertyMetadata en WPf) lorsqu’il est modifié renvoie vers la méthode OnSelectedItemsChanged

        public static void OnSelectedItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ListBox listBox = sender as ListBox;

            // desabonnement del'evenement d' Ajout/Retrait d'items selectionnés
            if (e.OldValue != null)
            {
                listBox.SelectionChanged -= new SelectionChangedEventHandler(listBox_SelectionChanged);
            }

            // initialisation des items selectionnées de la ListBox

            IList values = listBox.GetValue(SelectedItemsBehavior.SelectedItemsProperty) as IList;

            listBox.SelectedItems.Clear();

            foreach (object value in values)
            {
                listBox.SelectedItems.Add(value);
            }
            
            // abonnement Evenement d'Ajout/Retrait d'items selectionnés
            
            if (e.NewValue != null)
            {
                listBox.SelectionChanged += new SelectionChangedEventHandler(listBox_SelectionChanged);
            }
        }

Cette méthode est chargée d’initialiser la propriétés SelectedItems de la ListBox à partir de l’argument e.NewValue qui contient la collection Bindée (dans notre exemple SelectedStrings). Elle doit également s’abonner/désabonner à l’évenement SelectionChanged afin que SelectedStrings prennent en compte les changement de sélection dans la ListBox.

        static void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListBox listbox = sender as ListBox;

            // on récupère la collection contenue dans la propriété SelectedItems du Behavior
            IList values = listbox.GetValue(SelectedItemsBehavior.SelectedItemsProperty) as IList;

            foreach (object addedItem in e.AddedItems)
            {
                values.Add(addedItem);
            }

            foreach (object removedItem in e.RemovedItems)
            {
                values.Remove(removedItem);
            }
        }

Modification interne de SelectedString ?

Désormais nous pouvons mettre à jour SelectedStrings à partir de la propriété SelectedItems du Behavior !
Mais que se passera-t’il en cas de modification de SelectedStrings ?

et bien malheureusement rien, car notre Behavior ne prend pas en compte les changements internes de SelectedStrings.
En revanche en affectant SelectedStrings à une nouvelle valeur (différente de l’existante) les changements seront bien affichés !

	    // on instancie avec SelectedStrings
            this.SelectedStrings = new ObservableCollection<string>() { this.Strings[0] };	

Ce n’est pas parfait mais cela suffira dans la majorité des cas :)

Vous trouverez le source du behavior ici et une solution Silverlight/Wpf là.

Si cette solution ne couvre pas entièrement vos attentes (dans le cas cité plus haut) vous pouvez regarder cette autre solution, beaucoup plus complexe mais couvrant d’autres controles que la ListBox.

4 Responses to Binding de SelectedItems sur ListBox en WPF et Silverlight

  1.  

    Salut Samuel,

    Cela ressemble beaucoup à ce que fait Sebastien Alves dans son tutoriel : http://wpf-france.fr/articles/37-mvvm-listbox-multi-selection-synchronise-avec-le-viewmodel.html?catid=3%3Amvvm

    Intéressant à relire quand même… :)

  2. Salut Jonathan,

    Oui effectivement c’est le même principe.
    Je ne connaissais pas l’article (évidemment sinon cela serait du plagiat) et je te remercie de me l’avoir indiqué :)

     
  3.  

    Salut, merci pour ton code qui m’a vraiment beaucoup aidé dans mon problème.

    Je tiens à dire que ligne 104 de ton code global j’ai rajouté ceci :
    listbox.SetValue(SelectedItemsBehavior.SelectedItemsProperty, values);
    qui permet lors d’une désélection de remettre la collection à jour.

    Encore merci pour ton code, bonne continuation.

  4.  

    Merci ça fait un moment que je cherche une solution pour WP7.

    R

leave your comment