281 votes

Liaison de données d'une propriété enum à une zone de liste déroulante dans WPF

Comme un exemple, le code suivant:

public enum ExampleEnum { FooBar, BarFoo }

public class ExampleClass : INotifyPropertyChanged
{
    private ExampleEnum example;

    public ExampleEnum ExampleProperty 
    { get { return example; } { /* set and notify */; } }
}

Je veux un à databind la propriété ExampleProperty à une zone de liste déroulante, de sorte qu'il affiche les options "FooBar" et "BarFoo" et fonctionne en mode Bidirectionnel. Idéalement, je veux que mon ComboBox définition à ressembler à quelque chose comme ceci:

<ComboBox ItemsSource="What goes here?" SelectedItem="{Binding Path=ExampleProperty}" />

Actuellement, j'ai des gestionnaires pour la zone de liste déroulante.SelectionChanged et ExampleClass.PropertyChanged événements installé dans ma Fenêtre où je fais la liaison manuellement.

Est-il mieux ou une sorte de manière canonique? Souhaitez-vous généralement utiliser les Convertisseurs et comment feriez-vous pour remplir la zone de liste déroulante avec les valeurs de droite? Je ne veux pas même obtenir a commencé avec i18n droit maintenant.

Modifier

Alors une question a été répondu: Comment dois-je remplir la zone de liste déroulante avec les valeurs de droite.

Récupérer les valeurs de l'Enum comme une liste de chaînes de caractères via un ObjectDataProvider de la statique Enum.Méthode GetValues:

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

Ce que je peux utiliser comme un ItemsSource de ma zone de liste déroulante:

<ComboBox ItemsSource="{Binding Source={StaticResource ExampleEnumValues}}"/>

222voto

Gregor S. Points 1867

Vous pouvez créer une extension de balisage personnalisée.

Exemple d'utilisation:

 enum Status
{
    [Description("Available.")]
    Available,
    [Description("Not here right now.")]
    Away,
    [Description("I don't have time right now.")]
    Busy
}
 
 <ComboBox 
    ItemsSource="{Binding Source={my:Enumeration {x:Type my:Status}}}" 
    DisplayMemberPath="Description" 
    SelectedValue="{Binding CurrentStatus}"  
    SelectedValuePath="Value"  /> 
 

Et la mise en œuvre ...

 public class EnumerationExtension : MarkupExtension
  {
    private Type _enumType;


    public EnumerationExtension(Type enumType)
    {
      if (enumType == null)
        throw new ArgumentNullException("enumType");

      EnumType = enumType;
    }

    public Type EnumType
    {
      get { return _enumType; }
      private set
      {
        if (_enumType == value)
          return;

        var enumType = Nullable.GetUnderlyingType(value) ?? value;

        if (enumType.IsEnum == false)
          throw new ArgumentException("Type must be an Enum.");

        _enumType = value;
      }
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      var enumValues = Enum.GetValues(EnumType);

      return (
        from object enumValue in enumValues
        select new EnumerationMember{
          Value = enumValue,
          Description = GetDescription(enumValue)
        }).ToArray();
    }

    private string GetDescription(object enumValue)
    {
      var descriptionAttribute = EnumType
        .GetField(enumValue.ToString())
        .GetCustomAttributes(typeof (DescriptionAttribute), false)
        .FirstOrDefault() as DescriptionAttribute;


      return descriptionAttribute != null
        ? descriptionAttribute.Description
        : enumValue.ToString();
    }

    public class EnumerationMember
    {
      public string Description { get; set; }
      public object Value { get; set; }
    }
  }
 

207voto

user659130 Points 689

Dans le viewmodel, vous pouvez avoir:

     public MyEnumType SelectedMyEnumType 
    {
        get { return _selectedMyEnumType; }
        set { 
                _selectedMyEnumType = value;
                OnPropertyChanged("SelectedMyEnumType");
            }
    }

    public IEnumerable<MyEnumType> MyEnumTypeValues
    {
        get
        {
            return Enum.GetValues(typeof(MyEnumType))
                .Cast<MyEnumType>();
        }
    }
 

En XAML, ItemSource se lie à MyEnumTypeValues ​​et SelectedItem se lie à SelectedMyEnumType.

 <ComboBox SelectedItem="{Binding SelectedMyEnumType}" ItemsSource="{Binding MyEnumTypeValues}"></ComboBox>
 

123voto

CoperNick Points 484

Je préfère ne pas utiliser le nom d'enum dans l'interface utilisateur. Je préfère utiliser une valeur différente pour l'utilisateur ( DisplayMemberPath ) et différente pour la valeur (enum dans ce cas) ( SelectedValuePath ). Ces deux valeurs peuvent être groupées à KeyValuePair et stockées dans un dictionnaire.

XAML

 <ComboBox Name="fooBarComboBox" 
          ItemsSource="{Binding Path=ExampleEnumsWithCaptions}" 
          DisplayMemberPath="Value" 
          SelectedValuePath="Key"
          SelectedValue="{Binding Path=ExampleProperty, Mode=TwoWay}" > 
 

C #

 public Dictionary<ExampleEnum, string> ExampleEnumsWithCaptions
{
    get
    {
        return new Dictionary<ExampleEnum, string>() // Fix. Each time new dict.?
        {
            {ExampleEnum.FooBar, "Foo Bar"},
            {ExampleEnum.BarFoo, "Reversed Foo Bar"},
            //{ExampleEnum.None, "Hidden in UI"},
        };
    }
}


private ExampleEnum example;
public ExampleEnum ExampleProperty
{
    get { return example; }
    set { /* set and notify */; }
}
 

EDIT: Compatible avec le modèle MVVM.

44voto

rudigrobler Points 8883

Je ne sais pas si c'est possible en XAML seulement mais essayez ceci:

Donnez un nom à votre ComboBox pour pouvoir y accéder dans le codebehind: "typesComboBox1"

Maintenant, essayez ce qui suit

 typesComboBox1.ItemsSource = Enum.GetValues(typeof(ExampleEnum));
 

24voto

Martin Liversage Points 43712

Basé sur le acceptées, mais maintenant supprimé réponse fournie par ageektrapped j'ai créé une version allégée sans certaines des fonctionnalités les plus avancées. Tout le code est inclus ici pour vous permettre de copier-coller et de ne pas être bloqués par le lien de la pourriture.

J'utilise l' System.ComponentModel.DescriptionAttribute , ce qui est vraiment prévu pour le moment de la conception des descriptions. Si vous n'aimez pas l'aide de cet attribut, vous pouvez créer votre propre mais je pense que l'utilisation de cet attribut vraiment fait le travail. Si vous n'utilisez pas l'attribut le nom par défaut est le nom de la valeur d'enum dans le code.

public enum ExampleEnum
{
  [Description("Foo Bar")]
  FooBar,

  [Description("Bar Foo")]
  BarFoo
}

Ici, c'est la classe utilisée comme source des éléments:

public class EnumItemsSource : Collection<String>, IValueConverter {

  Type type;

  IDictionary<Object, Object> valueToNameMap;

  IDictionary<Object, Object> nameToValueMap;

  public Type Type {
    get { return this.type; }
    set {
      if (!value.IsEnum)
        throw new ArgumentException("Type is not an enum.", "value");
      this.type = value;
      Initialize();
    }
  }

  public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) {
    return this.valueToNameMap[value];
  }

  public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) {
    return this.nameToValueMap[value];
  }

  void Initialize() {
    this.valueToNameMap = this.type
      .GetFields(BindingFlags.Static | BindingFlags.Public)
      .ToDictionary(fi => fi.GetValue(null), GetDescription);
    this.nameToValueMap = this.valueToNameMap
      .ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    Clear();
    foreach (String name in this.nameToValueMap.Keys)
      Add(name);
  }

  static Object GetDescription(FieldInfo fieldInfo) {
    var descriptionAttribute =
      (DescriptionAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute));
    return descriptionAttribute != null ? descriptionAttribute.Description : fieldInfo.Name;
  }

}

Vous pouvez l'utiliser dans le code XAML comme ceci:

<Windows.Resources>
  <local:EnumItemsSource
    x:Key="ExampleEnumItemsSource"
    Type="{x:Type local:ExampleEnum}"/>
</Windows.Resources>
<ComboBox
  ItemsSource="{StaticResource ExampleEnumItemsSource}"
  SelectedValue="{Binding ExampleProperty, Converter={StaticResource ExampleEnumItemsSource}}"/> 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X