Мені подобається, що всі об'єкти, які я прив'язую, визначаються в моїх ViewModel
, тому я намагаюся уникати використання <ObjectDataProvider>
в xaml, коли це можливо.
У моєму рішенні немає даних, визначених у представленні даних, і немає коду. Лише DataBinding, ValueConverter для багаторазового використання, метод отримання колекції описів для будь-якого типу Enum та одне властивість у ViewModel, на яке пов'язується.
Коли я хочу , щоб прив'язати Enum
до ComboBox
тексту , який я хочу , щоб відобразити ніколи не відповідає значенням Enum
, тому я використовую [Description()]
атрибут , щоб дати йому текст , який я на самом деле хочу бачити в ComboBox
. Якби у мене були перелік днів тижня, це виглядало б приблизно так:
public enum DayOfWeek
{
// add an optional blank value for default/no selection
[Description("")]
NOT_SET = 0,
[Description("Sunday")]
SUNDAY,
[Description("Monday")]
MONDAY,
...
}
Спочатку я створив хелперний клас з парою методів боротьби із суперечками. Один метод отримує опис конкретного значення, інший отримує всі значення та їх описи для типу.
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;
// If no description is found, the least we can do is replace underscores with spaces
// You can add your own custom default formatting logic here
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();
}
}
Далі ми створюємо ValueConverter
. Успадкування від MarkupExtension
полегшує використання в XAML, тому нам не потрібно оголошувати це як ресурс.
[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;
}
}
Мені ViewModel
потрібна лише одна властивість, з якою мої View
можуть пов'язуватися як для комбінованої скриньки, так SelectedValue
і ItemsSource
для неї:
private DayOfWeek dayOfWeek;
public DayOfWeek SelectedDay
{
get { return dayOfWeek; }
set
{
if (dayOfWeek != value)
{
dayOfWeek = value;
OnPropertyChanged(nameof(SelectedDay));
}
}
}
І , нарешті , щоб зв'язати ComboBox
вид ( з використанням ValueConverter
в ItemsSource
зв'язуванні) ...
<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=SelectedDay}" />
Для реалізації цього рішення вам потрібно лише скопіювати мій EnumHelper
клас та EnumToCollectionConverter
клас. Вони працюватимуть з будь-якими перерахунками. Також я сюди не включив її, але ValueDescription
клас - це просто простий клас із двома властивостями загальнодоступних об'єктів, одним називаним Value
, одним викликаним Description
. Ви можете створити це самостійно або ви можете змінити код, щоб використовувати Tuple<object, object>
абоKeyValuePair<object, object>