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, ComboBoxle 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 MarkupExtensionfacilite 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 ViewModeln'a besoin que d'une propriété à laquelle my Viewpeut se lier à la fois pour le SelectedValueet ItemsSourcede 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 ComboBoxvue (en utilisant le ValueConverterdans la ItemsSourceliaison) ...
<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 EnumHelperclasse et ma EnumToCollectionConverterclasse. Ils fonctionneront avec toutes les énumérations. De plus, je ne l'ai pas inclus ici, mais la ValueDescriptionclasse 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>