En WPF, il est coutume de lier le contenu d’un contrôle avec une variable ou une classe. Cela se fait rapidement avec les binding. Seulement, lorsque l’on souhaite lier une liste déroulante à une énumération, c’est un peu plus compliqué. On va voir comment se passe un binding avec une énumération de deux façons différentes.

Pour notre exemple, prenons l’énumération suivante :

namespace DemoWPF {
    public enum Etat { 
        Use, 
        CommeNeuf, 
        Neuf
    }
}

La méthode « Rapide et sale »

Pour lier une liste déroulante, on peut utiliser un ObjectDataProvider directement dans le XAML.
La première chose à faire est de déclarer notre namespace et de déclarer le namespace System :

<Window x:Class="DemoWPF.MainWindow"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 xmlns:local="clr-namespace:DemoWPF"
 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 mc:Ignorable="d"
 Title="MainWindow" Height="350" Width="525">

Ensuite, on doit créer le DataObjectProvider, qui va convertir notre énumération en un tableau de string sur laquelle on fera notre binding (grâce à la fonction GetValues disponible sur les énumérations) :

<Window.Resources>
  <ObjectDataProvider x:Key="enumProvider" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
    <ObjectDataProvider.MethodParameters>
      <x:Type TypeName="local:Etat"/>
    </ObjectDataProvider.MethodParameters>
  </ObjectDataProvider>
</Window.Resources>

Enfin, il ne nous reste plus qu’à binder l’attribut ItemsSources à notre ObjectDataProvider :

<Grid>
    <ComboBox Margin="5" MinWidth="150" ItemsSource="{Binding Source={StaticResource enumProvider}}"/>
</Grid>

Et hopla ! Notre liste déroulante affiche nos valeurs de notre énumération.

Cependant, comme on peut le constater, la liste déroulante affiche notre énumération de manière « brute » (CommeNeuf, c’est pas très beau…). De plus, pour chacune de nos énumérations, il faut recréer un ObjectDataProvider, alourdissant notre XAML. C’est là qu’on utilise…

La méthode « Plus longue mais propre »

Cette méthode a l’avantage de ne plus déclarer d’ObjectDataProvider et de déporter toute la logique dans des classes séparées.

Pour ce faire, on va tout d’abord supprimer notre (nos) ObjectDataProvider. On garde cependant la déclaration de notre namespace.

On va créer une classe héritant de MarkupExtension, ce qui nous permettra de l’utiliser dans le XAML. Cette classe prendra en paramètre notre énumération pour en ressortir les valeurs au format string (toujours en faisant appel au GetValues) :

public class EnumBindingSourceExtension : MarkupExtension
{
    private Type _enumType;
    public Type EnumType
    {
        get { return this._enumType; }
        set
        {
            if (value != this._enumType)
            {
                if (null != value)
                {
                    Type enumType = Nullable.GetUnderlyingType(value) ?? value;

                    if (!enumType.IsEnum)
                        throw new ArgumentException("Le type doit être une énumération.");
                }

                this._enumType = value;
            }
        }
    }

    public EnumBindingSourceExtension() { }

    public EnumBindingSourceExtension(Type enumType)
    {
        this.EnumType = enumType;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (null == this._enumType)
            throw new InvalidOperationException("L'EnumType doit être spécifié.");

        Type actualEnumType = Nullable.GetUnderlyingType(this._enumType) ?? this._enumType;
        Array enumValues = Enum.GetValues(actualEnumType);

        if (actualEnumType == this._enumType)
            return enumValues;

        Array tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1);
        enumValues.CopyTo(tempArray, 1);
        return tempArray;
    }
}

Cette classe nous permet de simplifier notre XAML pour le même résultat :

<Grid>
    <ComboBox Margin="5" MinWidth="150" ItemsSource="{Binding Source={local:EnumBindingSource {x:Type local:Etat}}}"/>
</Grid>

Pour un résultat plus joli, il va falloir passer par un TypeConverter ainsi que des attributs de descriptions sur les valeurs de l’énumération :

public class EnumDescriptionTypeConverter : EnumConverter
{
    public EnumDescriptionTypeConverter(Type type): base(type)
    { }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            if (value != null)
            {
                FieldInfo fi = value.GetType().GetField(value.ToString());
                if (fi != null)
                {
                    var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    return ((attributes.Length > 0) && (!String.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : value.ToString();
                }
            }

            return string.Empty;
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

 

Une fois ce TypeConverter créé, il suffit de le rajouter sur notre énumération et d’ajouter des descriptions à nos valeurs :

namespace DemoEnum {
    [TypeConverter(typeof(EnumDescriptionTypeConverter))] 
    public enum Etat {
        [Description("Usé")]
        Use, 
        [Description("Comme neuf")]
        CommeNeuf, 
        [Description("Neuf")]
        Neuf 
    }
}

À partir de là, notre énumération est proprement affichée dans la liste !

Source : Brian Lagunas

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.