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}}"/>
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; }
}
}
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>
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.
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));
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}}"/>
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
vous pouvez considérer quelque chose comme ça:
-
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="<NULL>"/> <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>
-
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>
-
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.
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"
/>
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.
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}" ... />
essayer d'utiliser
<ComboBox ItemsSource="{Binding Source={StaticResource ExampleEnumValues}}"
SelectedValue="{Binding Path=ExampleProperty}" />
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:
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.