J'aime que tous les objets que je lie soient définis dans my ViewModel
, donc j'essaie d'éviter d'utiliser <ObjectDataProvider>
dans le xaml lorsque cela est possible.
Ma solution n'utilise aucune donnée définie dans la vue et aucun code-behind. Uniquement un DataBinding, un ValueConverter réutilisable, une méthode pour obtenir une collection de descriptions pour tout type Enum et une seule propriété dans le ViewModel à laquelle se lier.
Lorsque je veux lier un Enum
à un, ComboBox
le texte que je veux afficher ne correspond jamais aux valeurs de Enum
, donc j'utilise l' [Description()]
attribut pour lui donner le texte que je veux réellement voir dans le ComboBox
. Si j'avais une énumération des jours de la semaine, cela ressemblerait à ceci:
public enum DayOfWeek
{
[Description("")]
NOT_SET = 0,
[Description("Sunday")]
SUNDAY,
[Description("Monday")]
MONDAY,
...
}
J'ai d'abord créé une classe d'assistance avec quelques méthodes pour gérer les énumérations. Une méthode obtient une description pour une valeur spécifique, l'autre méthode obtient toutes les valeurs et leurs descriptions pour un type.
public static class EnumHelper
{
public static string Description(this Enum value)
{
var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Any())
return (attributes.First() as DescriptionAttribute).Description;
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " ")));
}
public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t)
{
if (!t.IsEnum)
throw new ArgumentException($"{nameof(t)} must be an enum type");
return Enum.GetValues(t).Cast<Enum>().Select((e) => new ValueDescription() { Value = e, Description = e.Description() }).ToList();
}
}
Ensuite, nous créons un fichier ValueConverter
. L'héritage de MarkupExtension
facilite son utilisation en XAML, nous n'avons donc pas à le déclarer en tant que ressource.
[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return EnumHelper.GetAllValuesAndDescriptions(value.GetType());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
My ViewModel
n'a besoin que d'une propriété à laquelle my View
peut se lier à la fois pour le SelectedValue
et ItemsSource
de la zone de liste déroulante:
private DayOfWeek dayOfWeek;
public DayOfWeek SelectedDay
{
get { return dayOfWeek; }
set
{
if (dayOfWeek != value)
{
dayOfWeek = value;
OnPropertyChanged(nameof(SelectedDay));
}
}
}
Et enfin pour lier la ComboBox
vue (en utilisant le ValueConverter
dans la ItemsSource
liaison) ...
<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=SelectedDay}" />
Pour implémenter cette solution, il vous suffit de copier ma EnumHelper
classe et ma EnumToCollectionConverter
classe. Ils fonctionneront avec toutes les énumérations. De plus, je ne l'ai pas inclus ici, mais la ValueDescription
classe est juste une classe simple avec 2 propriétés d'objet public, une appelée Value
, une appelée Description
. Vous pouvez le créer vous-même ou modifier le code pour utiliser un Tuple<object, object>
ouKeyValuePair<object, object>