La liaison de données enum propriété à un ComboBox dans WPF

à titre d'exemple, prendre 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 une base de données de la propriété Exampleproprity à un ComboBox, de sorte qu'il affiche les options" FooBar "et" BarFoo " et fonctionne en Mode TwoWay. De façon optimale, Je veux que ma définition de ComboBox ressemble à quelque chose comme ceci:

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

J'ai des gestionnaires pour le ComboBox.Selection changed et ExampleClass.Événements modifiés par la propriété installés dans ma fenêtre où je fais la reliure manuellement.

Est-il mieux ou une sorte de manière canonique? Utiliseriez-vous habituellement des convertisseurs et comment rempliriez-vous le ComboBox avec les bonnes valeurs? Je ne veux même pas commencer avec i18n maintenant.

Modifier

ainsi une question a été répondue: Comment puis-je peupler le ComboBox avec les bonnes valeurs.

récupère les valeurs Enum comme une liste de chaînes via un ObjectDataProvider de l'Enum statique.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>

je peux l'utiliser comme source Itemsspour mon ComboBox:

<ComboBox ItemsSource="{Binding Source={StaticResource ExampleEnumValues}}"/>
230
demandé sur Maximilian 2008-09-12 15:38:18

13 réponses

vous pouvez créer une extension de markup personnalisée.

exemple d'usage:

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; }
    }
  }
195
répondu Gregor Slavec 2012-02-11 18:16:39

dans le modèle de vue 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>();
        }
    }

dans XAML, ItemSource se lie à MyEnumTypeValues et SelectedItem se lie à SelectedMyEnumType.

<ComboBox SelectedItem="{Binding SelectedMyEnumType}" ItemsSource="{Binding MyEnumTypeValues}"></ComboBox>
155
répondu user659130 2012-03-30 06:01:59

je préfère ne pas utiliser le nom d'enum dans L'UI. Je préfère utiliser une valeur différente pour l'utilisateur ( DisplayMemberPath ) et de valeur (enum dans ce cas) ( SelectedValuePath ). Ces deux valeurs peuvent être emballées à KeyValuePair et stockées dans le 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.

67
répondu CoperNick 2012-09-20 14:02:12

Je ne sais pas si C'est possible dans XAML-seulement mais essayez ce qui suit:

donnez un nom à votre ComboBox pour que vous puissiez y accéder dans le codebehind: "typesComboBox1"

essayez maintenant ce qui suit

typesComboBox1.ItemsSource = Enum.GetValues(typeof(ExampleEnum));
39
répondu rudigrobler 2008-09-12 11:54:40

basé sur la réponse acceptée mais maintenant supprimé fourni par ageektrapped j'ai créé une version réduite vers le bas sans certaines des fonctionnalités plus avancées. Tout le code est inclus ici pour vous permettre de le copier-coller et de ne pas être bloqué par link-rot.

j'utilise le System.ComponentModel.DescriptionAttribute qui est vraiment destiné à des descriptions de temps de conception. Si vous n'aimez pas utiliser cet attribut vous pouvez créer votre propre, mais je pense que l'utilisation de cet attribut obtient vraiment le travail faire. Si vous n'utilisez pas l'attribut, le nom sera par défaut au nom de la valeur enum dans le code.

public enum ExampleEnum {

  [Description("Foo Bar")]
  FooBar,

  [Description("Bar Foo")]
  BarFoo

}

Voici la classe utilisée comme les articles source:

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 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}}"/> 
23
répondu Martin Liversage 2015-09-03 23:14:36

Use ObjectDataProvider:

<ObjectDataProvider x:Key="enumValues"
   MethodName="GetValues" ObjectType="{x:Type System:Enum}">
      <ObjectDataProvider.MethodParameters>
           <x:Type TypeName="local:ExampleEnum"/>
      </ObjectDataProvider.MethodParameters>
 </ObjectDataProvider>

et puis lier à la ressource statique:

ItemsSource="{Binding Source={StaticResource enumValues}}"

trouver cette "solution sur ce blog

19
répondu druss 2015-01-27 14:58:30

vous pouvez considérer quelque chose comme ça:

  1. définissez un style de texte ou tout autre contrôle que vous voulez utiliser pour afficher votre enum:

        <Style x:Key="enumStyle" TargetType="{x:Type TextBlock}">
            <Setter Property="Text" Value="&lt;NULL&gt;"/>
            <Style.Triggers>
                <Trigger Property="Tag">
                    <Trigger.Value>
                        <proj:YourEnum>Value1<proj:YourEnum>
                    </Trigger.Value>
                    <Setter Property="Text" Value="{DynamicResource yourFriendlyValue1}"/>
                </Trigger>
                <!-- add more triggers here to reflect your enum -->
            </Style.Triggers>
        </Style>
    
  2. définissez votre style pour ComboBoxItem

        <Style TargetType="{x:Type ComboBoxItem}">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <TextBlock Tag="{Binding}" Style="{StaticResource enumStyle}"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
  3. ajouter un combobox et le charger avec vos valeurs enum:

            <ComboBox SelectedValue="{Binding Path=your property goes here}" SelectedValuePath="Content">
                <ComboBox.Items>
                    <ComboBoxItem>
                        <proj:YourEnum>Value1</proj:YourEnum>
                    </ComboBoxItem>
                </ComboBox.Items>
            </ComboBox>
    

si votre enum est grande, vous pouvez bien sûr faire la même chose dans le code, épargnant beaucoup de saisie. j'aime cette approche, car elle rend la localisation facile - vous définissez tous les gabarits une fois, et puis, vous mettez à jour seulement vos fichiers de ressources de chaîne.

5
répondu Greg 2008-09-16 16:06:52

Voici une solution générique utilisant une méthode d'aide. Cela peut également gérer un enum de n'importe quel type sous-jacent (byte, sbyte, uint, long, etc.)

Méthode D'Assistance:

static IEnumerable<object> GetEnum<T>() {
    var type    = typeof(T);
    var names   = Enum.GetNames(type);
    var values  = Enum.GetValues(type);
    var pairs   =
        Enumerable.Range(0, names.Length)
        .Select(i => new {
                Name    = names.GetValue(i)
            ,   Value   = values.GetValue(i) })
        .OrderBy(pair => pair.Name);
    return pairs;
}//method

Modèle De Vue:

public IEnumerable<object> EnumSearchTypes {
    get {
        return GetEnum<SearchTypes>();
    }
}//property

zone de liste déroulante:

<ComboBox
    SelectedValue       ="{Binding SearchType}"
    ItemsSource         ="{Binding EnumSearchTypes}"
    DisplayMemberPath   ="Name"
    SelectedValuePath   ="Value"
/>
3
répondu Jack 2013-02-20 11:35:25

ma façon préférée de le faire est avec un ValueConverter de sorte que L'ItemsSource et SelectedValue se lient tous deux à la même propriété. Cela nécessite pas de propriétés supplémentaires pour garder votre modèle beau et propre.

<ComboBox ItemsSource="{Binding Path=ExampleProperty, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
          SelectedValuePath="Value"
          DisplayMemberPath="Description"
          SelectedValue="{Binding Path=ExampleProperty}" />

et la définition du convertisseur:

public static class EnumHelper
{
  public static string Description(this Enum e)
  {
    return (e.GetType()
             .GetField(e.ToString())
             .GetCustomAttributes(typeof(DescriptionAttribute), false)
             .FirstOrDefault() as DescriptionAttribute)?.Description ?? e.ToString();
  }
}

[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return Enum.GetValues(value.GetType())
               .Cast<Enum>()
               .Select(e => new ValueDescription() { Value = e, Description = e.Description()})
               .ToList();
  }
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return null;
  }
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    return this;
  }
}

ce convertisseur fonctionnera avec n'importe quel enum. ValueDescription est juste une classe simple avec un Value propriété et un Description propriété. Vous pouvez tout aussi facilement utiliser un Tuple avec Item1 et Item2 , ou un KeyValuePair avec Key et Value au lieu de valeur et de Description ou de toute autre classe de votre choix aussi longtemps qu'il a une valeur enum et une description de chaîne de cette valeur enum.

3
répondu Nick 2017-07-13 21:58:01

si vous utilisez un MVVM, basé sur la réponse de @rudigrobler, vous pouvez faire ce qui suit:

Ajouter la propriété suivante à la ViewModel classe

public Array ExampleEnumValues => Enum.GetValues(typeof(ExampleEnum));

puis dans le XAML faire ce qui suit:

<ComboBox ItemsSource="{Binding ExampleEnumValues}" ... />
1
répondu MotKohn 2017-04-26 13:30:28

essayer d'utiliser

<ComboBox ItemsSource="{Binding Source={StaticResource ExampleEnumValues}}"
    SelectedValue="{Binding Path=ExampleProperty}" />
0
répondu rudigrobler 2008-09-12 12:46:22

C'est un DevExpress réponse spécifique basé sur le haut-voté répondre par Gregor S. (actuellement, il dispose de 128 voix).

cela signifie que nous pouvons garder le style cohérent sur l'ensemble de l'application:

enter image description here

malheureusement, la réponse originale ne fonctionne pas avec un ComboBoxEdit de DevExpress sans quelques modifications.

D'abord, le XAML pour le ComboBoxEdit :

<dxe:ComboBoxEdit ItemsSource="{Binding Source={xamlExtensions:XamlExtensionEnumDropdown {x:myEnum:EnumFilter}}}"
    SelectedItem="{Binding BrokerOrderBookingFilterSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    DisplayMember="Description"
    MinWidth="144" Margin="5" 
    HorizontalAlignment="Left"
    IsTextEditable="False"
    ValidateOnTextInput="False"
    AutoComplete="False"
    IncrementalFiltering="True"
    FilterCondition="Like"
    ImmediatePopup="True"/>

inutile de dire, vous devrez pointer xamlExtensions à l'Espace-nom qui contient la classe extension XAML (qui est définie ci-dessous):

xmlns:xamlExtensions="clr-namespace:XamlExtensions"

et nous devons pointer myEnum à l'Espace-nom qui contient l'enum:

xmlns:myEnum="clr-namespace:MyNamespace"

puis, l'enum:

namespace MyNamespace
{
    public enum EnumFilter
    {
        [Description("Free as a bird")]
        Free = 0,

        [Description("I'm Somewhat Busy")]
        SomewhatBusy = 1,

        [Description("I'm Really Busy")]
        ReallyBusy = 2
    }
}

le problème avec le XAML est que nous ne pouvons pas utiliser SelectedItemValue , comme ceci lance une erreur car le setteur est inexplicable (un petit oubli de votre part, DevExpress ). Nous devons donc modifier notre ViewModel pour obtenir la valeur directement à partir de l'objet:

private EnumFilter _filterSelected = EnumFilter.All;
public object FilterSelected
{
    get
    {
        return (EnumFilter)_filterSelected;
    }
    set
    {
        var x = (XamlExtensionEnumDropdown.EnumerationMember)value;
        if (x != null)
        {
            _filterSelected = (EnumFilter)x.Value;
        }
        OnPropertyChanged("FilterSelected");
    }
}

pour être complet, voici L'extension XAML de la réponse originale (légèrement renommée):

namespace XamlExtensions
{
    /// <summary>
    ///     Intent: XAML markup extension to add support for enums into any dropdown box, see http://bit.ly/1g70oJy. We can name the items in the
    ///     dropdown box by using the [Description] attribute on the enum values.
    /// </summary>
    public class XamlExtensionEnumDropdown : MarkupExtension
    {
        private Type _enumType;


        public XamlExtensionEnumDropdown(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();
        }

        #region Nested type: EnumerationMember
        public class EnumerationMember
        {
            public string Description { get; set; }
            public object Value { get; set; }
        }
        #endregion
    }
}

clause de non-responsabilité: Je n'ai aucune affiliation avec DevExpress. Telerik est aussi une grande bibliothèque.

0
répondu Contango 2015-06-30 16:01:49

j'ai créé un open source CodePlex projet qui fait ceci. Vous pouvez télécharger le paquet NuGet de ici .

<enumComboBox:EnumComboBox EnumType="{x:Type demoApplication:Status}" SelectedValue="{Binding Status}" />
0
répondu LawMan 2016-11-10 21:41:04