• Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 29/04/2014

ListBox

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

La ListBox est un élément incontournable dans la création d’applications pour Windows Phone. Elle permet un puissant affichage d’une liste d’élément. Voyons tout de suite de quoi il s’agit car vous allez vous en servir très souvent !

Un contrôle majeur

Utilisons notre designer préféré pour rajouter une ListBox dans notre page et nommons-là ListeDesTaches, ce qui donne le XAML suivant :

<ListBox x:Name="ListeDesTaches">
</ListBox>

Une ListBox permet d’afficher des éléments sous la forme d’une liste. Pour ajouter des éléments, on peut utiliser le XAML suivant :

<ListBox x:Name="ListeDesTaches">
    <ListBoxItem Content="Arroser les plantes" />
    <ListBoxItem Content="Tondre le gazon" />
    <ListBoxItem Content="Planter les tomates" />
</ListBox>

Ce qui donne la figure suivante.

Une ListBox contenant 3 éléments positionnés par XAML
Une ListBox contenant 3 éléments positionnés par XAML

Il est cependant plutôt rare d’énumérer les éléments d’une ListBox directement dans le XAML. En général, ces éléments viennent d’une source dynamique construite depuis le code behind. Pour cela, il suffit d’alimenter la propriété ItemSource avec un objet implémentant IEnumerable, par exemple une liste. Supprimons les ListBoxItem de notre ListBox et utilisons ce code behind :

List<string> chosesAFaire = new List<string>
{
        "Arroser les plantes",
        "Tondre le gazon",
        "Planter les tomates"
};
ListeDesTaches.ItemsSource = chosesAFaire;

Il ne reste plus qu’à démarrer l’application. Nous pouvons voir que la ListBox s’est automatiquement remplie avec nos valeurs (voir la figure suivante).

La ListBox est remplie depuis le code-behind
La ListBox est remplie depuis le code-behind

Et ceci sans rien faire de plus. Ce qu’il est important de remarquer, c’est que si nous ajoutons beaucoup d’éléments à notre liste, alors celle-ci gère automatiquement un ascenseur pour pouvoir faire défiler la liste. La ListBox est donc une espèce de ScrollViewer qui contient une liste d’éléments dans un StackPanel. Tout ceci est géré nativement par la ListBox.

Nous avons quand même rencontré un petit truc étrange. Dans le XAML, nous avons mis la ListBox, mais la liste est vide, rien ne s’affiche dans le designer. Ce qui n’est pas très pratique pour créer notre page. C’est logique car l’alimentation de la ListBox est faite dans le constructeur, c’est-à-dire lorsque nous démarrons notre application. Nous verrons plus loin comment y remédier.
L’autre souci, c’est que si vous essayez de mettre des choses un peu plus complexes qu’une chaine de caractère dans la ListBox, par exemple un objet :

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();

        List<ElementAFaire> chosesAFaire = new List<ElementAFaire>
        {
            new ElementAFaire { Priorite = 1, Description = "Arroser les plantes"},
            new ElementAFaire { Priorite = 2, Description = "Tondre le gazon"},
            new ElementAFaire { Priorite = 1, Description = "Planter les tomates"},
            new ElementAFaire { Priorite = 3, Description = "Laver la voiture"},
        };
        ListeDesTaches.ItemsSource = chosesAFaire.OrderBy(e => e.Priorite);
    }
}

public class ElementAFaire
{
    public int Priorite { get; set; }
    public string Description { get; set; }
}

vous aurez l’affichage suivant (voir la figure suivante).

C'est la représentation de l'objet qui s'affiche ici dans la ListBox
C'est la représentation de l'objet qui s'affiche ici dans la ListBox

La ListBox affiche la représentation de l’objet, c’est-à-dire le résultat de sa méthode ToString(). Ce qui est un peu moche ici.
Vous me direz, il suffit de substituer la méthode ToString() avec quelque chose comme ça :

public class ElementAFaire
{
    public int Priorite { get; set; }
    public string Description { get; set; }

    public override string ToString()
    {
        return Priorite + " - " + Description;
    }
}

Et l’affaire est réglée ! Et je vous répondrai oui, parfait. Sauf que cela ne fonctionne que parce que nous affichons du texte ! Et si nous devions afficher du texte et une image ? Ou du texte et un bouton ?

Gérer les modèles

C’est là qu’interviennent les modèles, plus couramment appelés en anglais : template.
Ils permettent de personnaliser le rendu de son contrôle. Le contrôle garde toute sa logique mais peut nous confier le soin de gérer l’affichage, si nous le souhaitons. C’est justement ce que nous voulons faire.

Nous allons donc redéfinir l’affichage de chaque élément de la liste. Pour cela, plutôt que d’afficher un simple texte, nous allons en profiter pour afficher une image pour la priorité et le texte de la description. Si la priorité est égale à 1, alors nous afficherons un rond rouge, sinon un rond vert (voir la figure suivante).

Image rouge
Image rouge
Image verte
Image verte

Créez un répertoire Images sous le répertoire Assets par exemple et ajoutez les deux images en tant que Contenu, comme nous l’avons déjà fait. Vous pouvez les télécharger la rouge ici, et la verte .

La première chose à faire est de définir le modèle des éléments de la ListBox. Cela se fait avec le code suivant :

<ListBox x:Name="ListeDesTaches">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Image Source="{Binding Image}" Width="30" Height="30" />
                <TextBlock Text="{Binding Description}" Margin="20 0 0 0" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

On redéfinit la propriété ItemTemplate, c’est-à-dire le modèle d’un élément de la liste. Puis à l’intérieur de la balise DataTemplate, on peut rajouter nos propres contrôles. Ici, il y a un conteneur, le StackPanel, qui contient une image et une zone de texte en lecture seule. Le DataTemplate doit toujours contenir un seul contrôle.
Vu que vous avez l’œil, vous avez remarqué des extensions de balisage XAML à l’intérieur du contrôle Image et du contrôle TextBlock. Je vais y revenir dans le prochain chapitre.

En attendant, nous allons modifier légèrement le code behind de cette façon :

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();

        List<ElementAFaire> chosesAFaire = new List<ElementAFaire>
        {
            new ElementAFaire { Priorite = 1, Description = "Arroser les plantes"},
            new ElementAFaire { Priorite = 2, Description = "Tondre le gazon"},
            new ElementAFaire { Priorite = 1, Description = "Planter les tomates"},
            new ElementAFaire { Priorite = 3, Description = "Laver la voiture"},
        };

        ListeDesTaches.ItemsSource = chosesAFaire.OrderBy(e => e.Priorite).Select(e => new ElementAFaireBinding { Description = e.Description, Image = ObtientImage(e.Priorite) });
    }

    private BitmapImage ObtientImage(int priorite)
    {
        if (priorite <= 1)
            return new BitmapImage(new Uri("/Assets/Images/vert.png", UriKind.Relative));
        return new BitmapImage(new Uri("/Assets/Images/rouge.png", UriKind.Relative));
    }
}

Pour rappel, la classe BitmapImage se trouve dans l’espace de nom System.Windows.Media.Imaging. Vérifiez également que le l’URL passée à la classe BitmapImage correspond à l’emplacement où vous avez ajouté les images.

Nous aurons besoin de la nouvelle classe suivante :

public class ElementAFaireBinding
{
    public BitmapImage Image { get; set; }
    public string Description { get; set; }
}

Le principe est de construire des éléments énumérables à partir de notre liste. Il s’agit d’y mettre un nouvel objet qui possède une propriété Description et une propriété Image qui contient un objet BitmapImage construit à partir de la valeur de la priorité de la tâche.
Il est important de constater que la classe contient des propriétés qui ont les mêmes noms que ce qu'on a écrit dans l’extension de balisage vue plus haut.

<Image Source="{Binding Image}" Width="30" Height="30" />
<TextBlock Text="{Binding Description}" Margin="20 0 0 0" />

J’y reviendrai plus tard, mais nous avons ici fait ce qu’on appelle un binding, que l’on peut traduire par une liaison de données. Nous indiquons que nous souhaitons mettre la valeur de la propriété Image de l’élément courant dans la propriété Source de l’image et la valeur de la propriété Description de l’élément courant dans la propriété Text du TextBlock. Rappelez-vous, l’élément courant est justement un objet spécial qui contient ces propriétés.

Si nous exécutons le code, nous obtenons donc la figure suivante.

Les images s'affichent dans la ListBox grâce au modèle
Les images s'affichent dans la ListBox grâce au modèle

Magique ! Le seul défaut viendrait de mes images qui ne sont pas transparentes…

Sachez qu’il existe beaucoup de contrôles qui utilisent ce même mécanisme de modèle, nous aurons l’occasion d’en voir d’autres. C’est une fonctionnalité très puissante qui nous laisse beaucoup de contrôle sur le rendu de nos données.

Sélection d’un élément

La ListBox gère également un autre point intéressant : savoir quel élément est sélectionné. Cela se fait en toute logique grâce à un événement. Pour que cela soit plus simple, enlevons nos templates et modifions le XAML pour avoir :

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="100" />
    </Grid.RowDefinitions>
    <ListBox x:Name="ListeDesTaches" SelectionChanged="ListeDesTaches_SelectionChanged">
    </ListBox>
    <TextBlock x:Name="Selection" Grid.Row="1" />
</Grid>

Notre grille a donc deux lignes, la première contenant la ListBox et la seconde un TextBlock. Remarquons l’événement SelectionChanged qui est associé à la méthode ListeDesTaches_SelectionChanged. Dans le code behind, nous aurons :

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();
        List<string> chosesAFaire = new List<string>
        {
            "Arroser les plantes",
            "Tondre le gazon",
            "Planter les tomates"
        };
        ListeDesTaches.ItemsSource = chosesAFaire;
    }

    private void ListeDesTaches_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count > 0)
            Selection.Text = e.AddedItems[0].ToString();
    }
}

Ce qui va nous permettre d’afficher dans le TextBlock la valeur de ce que nous avons choisi (voir la figure suivante).

Élément sélectionné dans la ListBox
Élément sélectionné dans la ListBox

Nous voyons au passage que la sélection est mise en valeur automatiquement dans la ListBox.

Remarquez qu’étant donné que notre ListBox a un nom et donc une variable pour la manipuler, il est également d’obtenir la valeur sélectionnée grâce à l’instruction suivante :

private void ListeDesTaches_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Selection.Text = ListeDesTaches.SelectedItem.ToString();
}

Ce qui est d’ailleurs plus propre.
L’objet SelectedItem est du type object et sera du type de ce que nous avons mis dans la propriété ItemsSource. Étant donné que nous avons mis une liste de chaine de caractères, SelectedItem sera une chaine, nous pouvons donc faire :

private void ListeDesTaches_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Selection.Text = (string)ListeDesTaches.SelectedItem;
}

De la même façon, l’index de l’élément sélectionné sera accessible grâce à la propriété SelectedIndex et sera du type entier.
Ce qui permet par exemple de présélectionner une valeur dans notre ListBox au chargement de celle-ci. Ainsi, pour sélectionner le deuxième élément, je pourrais faire :

public MainPage()
{
    InitializeComponent();
    List<string> chosesAFaire = new List<string>
    {
        "Arroser les plantes",
        "Tondre le gazon",
        "Planter les tomates"
    };
    ListeDesTaches.ItemsSource = chosesAFaire;
    ListeDesTaches.SelectedIndex = 1;
}

Ou encore :

public MainPage()
{
    InitializeComponent();
    List<string> chosesAFaire = new List<string>
    {
        "Arroser les plantes",
        "Tondre le gazon",
        "Planter les tomates"
    };
    ListeDesTaches.ItemsSource = chosesAFaire;
    ListeDesTaches.SelectedValue = chosesAFaire[1];
}

sachant que le fait d’initialiser la sélection d’un élément par code déclenche l’événement de changement de sélection ; ce qui nous arrange pour que notre TextBlock soit rempli. Pour éviter ceci, il faudrait associer la méthode à l’événement de changement de sélection après avoir sélectionné l’élément. Cela revient à enlever la définition de l’événement dans le XAML :

<ListBox x:Name="ListeDesTaches">
</ListBox>

Et à s’abonner à l’événement depuis le code behind, après avoir initialisé l’élément sélectionné :

public MainPage()
{
    InitializeComponent();
    List<string> chosesAFaire = new List<string>
    {
        "Arroser les plantes",
        "Tondre le gazon",
        "Planter les tomates"
    };
    ListeDesTaches.ItemsSource = chosesAFaire;
    ListeDesTaches.SelectedValue = chosesAFaire[1];
    ListeDesTaches.SelectionChanged += ListeDesTaches_SelectionChanged;
}

Pour finir sur la sélection d’un élément, il faut savoir que la ListBox peut permettre de sélectionner plusieurs éléments en changeant sa propriété SelectionMode et en la passant à Multiple :

<ListBox x:Name="ListeDesTaches" SelectionChanged="ListeDesTaches_SelectionChanged" SelectionMode="Multiple">
</ListBox>

Et nous pourrons récupérer les différentes valeurs sélectionnées grâce à la collection SelectedItems :

private void ListeDesTaches_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    string selection = string.Empty;
    foreach (string choix in ListeDesTaches.SelectedItems)
    {
        selection += choix + ";";
    }
    Selection.Text = selection;
}

Très bien tout ça. :)

  • La ListBox est un contrôle très utile qui permet d’afficher une liste d’éléments très facilement.

  • Il est possible de personnaliser efficacement le rendu de chaque élément d’une ListBox grâce au système de modèles.

  • La ListBox est un contrôle complet qui possède tout une gestion de l’élément sélectionné.

Exemple de certificat de réussite
Exemple de certificat de réussite