Template conditionnel d'une listbox WPF

1) Introduction

Il existe de multiples façons de réaliser un changement de rendu d'une ListBox WPF. Il est souvent possible de se limiter à la définition d'un Template mais ce n'est pas toujours possible. Voici donc différentes pistes pour réaliser ce changement de rendu.

Nous avons créé un petit jeu de données avec différentes classes:

Diagramme de classes

L'utilisation des méthodes décrites dans les points 3,4 ou 5 sera rendue sous cette forme:

Rendu différent selon le template

2) Définition d'un template unique

L'implémentation d'un template pour une ListBox passe par la définition de la propriété ItemTemplate disponible dans la classe ItemsControl. Nous allons donc "binder" une collection de différents types sur le DataContext de la Window et définir l'ItemsSource comme étant le DataContext.

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:custom="clr-namespace:WpfApplication"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <StackPanel Width="100px" Background="Azure" Orientation="Vertical">
                    <CheckBox IsChecked="{Binding EstSexeMasculin}"/>
                </StackPanel>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
 
        ObservableCollection<EtreVivant> collection = new ObservableCollection<EtreVivant>()
        {
            new Animal() { EstSexeMasculin = true, Genre = "Mammifère" },
            new Humain() { EstSexeMasculin = true, CouleurYeux = "Bleu" },
            new AnimalMarin(){ EstSexeMasculin = false, Genre = "Mammifère", Profondeur = 200.5 },
        };
        this.DataContext = collection;
    }
}

3) Définition d'un template selon le type d'objet

Souvent, on souhaite rendre l'objet différemment selon le type. En effet, l'héritage ajoute des propriétés et comportements qu'il faut afficher autrement. Pour ce faire, nous allons utiliser la même collection que celle définie dans le point 1. Nous allons simplement supprimer l'ItemTemplate et le remplacer par des DataTemplate dans l'élément ListBox.Resources.

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:custom="clr-namespace:WpfApplication"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding}">
            <ListBox.Resources>
                <DataTemplate DataType="{x:Type custom:EtreVivant}">
                    <StackPanel Width="100px" Background="Azure" Orientation="Vertical">
                        <CheckBox IsChecked="{Binding EstSexeMasculin}"/>
                    </StackPanel>
                </DataTemplate>
                <DataTemplate DataType="{x:Type custom:Humain}">
                    <StackPanel Width="100px" Background="Green" Orientation="Vertical">
                        <CheckBox IsChecked="{Binding EstSexeMasculin}"/>
                        <TextBox Text="{Binding CouleurYeux}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.Resources>
        </ListBox>
    </Grid>
</Window>

4) Implémentation d'un Selecteur de DataTemplate et utilisation XAML

Parfois, il est indispensable de rendre différemment un même type selon certains critères. Nous pouvons donc afficher un objet en "visualisation" et un autre en "édition" selon la valeur d'une propriété. Cependant, dans l'exemple, je montre comment réaliser le point 2 mais via ce mécanisme décrit ci-dessus.

a) Nous allons donc implémenter la classe de selection du DataTemplate

public class DataTemplateSelectorExample : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item == null)
            return null;
 
        Window window = App.Current.MainWindow;
 
        if (item is Humain)
            return window.FindResource("templateHumain") as DataTemplate;
 
        return window.FindResource("templateEtreVivant") as DataTemplate;
    }
}

b) Nous allons donc appeler cette classe définie dans le point a dans le code XAML

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:custom="clr-namespace:WpfApplication"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <custom:DataTemplateSelectorExample x:Key="selector"/>
        <DataTemplate x:Key="templateEtreVivant">
            <StackPanel Width="100px" Background="Azure" Orientation="Vertical">
                <CheckBox IsChecked="{Binding EstSexeMasculin}"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="templateHumain">
            <StackPanel Width="100px" Background="Green" Orientation="Vertical">
                <CheckBox IsChecked="{Binding EstSexeMasculin}"/>
                <TextBox Text="{Binding CouleurYeux}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource selector}">
        </ListBox>
    </Grid>
</Window>

5) Implémentation d'un Selecteur de DataTemplate et utilisation c#

Cette méthode offre la possibilité de passer dans le constructeur un ou des objets. Cela permet de rechercher les ressources sur un objet particulier,etc.

a) Nous allons donc implémenter la classe de selection du DataTemplate

public class DataTemplateSelectorExample : DataTemplateSelector
{
    private FrameworkElement element;
 
    public DataTemplateSelectorExample(FrameworkElement element)
    {
        this.element = element;
    }
 
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item == null)
            return null;
 
        if (item is Humain)
            return element.FindResource("templateHumain") as DataTemplate;
 
        return element.FindResource("templateEtreVivant") as DataTemplate;
    }
}

b) Nous allons donc définir les templates dans la Window mais nous n'allons pas l'enregistrer dans le code XAML

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:custom="clr-namespace:WpfApplication"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="templateEtreVivant">
            <StackPanel Width="100px" Background="Azure" Orientation="Vertical">
                <CheckBox IsChecked="{Binding EstSexeMasculin}"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="templateHumain">
            <StackPanel Width="100px" Background="Green" Orientation="Vertical">
                <CheckBox IsChecked="{Binding EstSexeMasculin}"/>
                <TextBox Text="{Binding CouleurYeux}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource selector}">
        </ListBox>
    </Grid>
</Window>

c) Nous allons l'enregistrer dans le constructeur de la Window avant l'initialisation des composants.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.Resources["selector"] = new DataTemplateSelectorExample(this);
        InitializeComponent();
        (...)
    }
}

6) Conclusion

Vous avez donc pu voir ce qui se fait dans le template conditionnel. N'hésitez pas à me faire part de vos solutions ...

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

Fil des commentaires de ce billet

Page top