Мне нравится, чтобы все объекты, которые я связываю, были определены в моем 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
потребности только 1 свойство , что моя 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>