Tôi thích tất cả các đối tượng mà tôi ràng buộc được xác định trong tôi ViewModel
, vì vậy tôi cố gắng tránh sử dụng <ObjectDataProvider>
trong xaml khi có thể.
Giải pháp của tôi sử dụng không có dữ liệu được xác định trong Chế độ xem và không có mã phía sau. Chỉ một DataBinding, ValueConverter có thể sử dụng lại, một phương thức để có được một bộ mô tả cho bất kỳ loại Enum nào và một thuộc tính duy nhất trong ViewModel để liên kết.
Khi tôi muốn để ràng buộc một Enum
đến một ComboBox
văn bản tôi muốn hiển thị không bao giờ phù hợp với các giá trị của Enum
, vì vậy tôi sử dụng các [Description()]
thuộc tính để cho nó văn bản mà tôi thực sự muốn nhìn thấy trong ComboBox
. Nếu tôi có một số ngày trong tuần, nó sẽ trông giống như thế này:
public enum DayOfWeek
{
// add an optional blank value for default/no selection
[Description("")]
NOT_SET = 0,
[Description("Sunday")]
SUNDAY,
[Description("Monday")]
MONDAY,
...
}
Đầu tiên tôi tạo lớp trợ giúp với một vài phương thức để đối phó với enums. Một phương thức nhận được một mô tả cho một giá trị cụ thể, phương thức kia nhận tất cả các giá trị và mô tả của chúng cho một loại.
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();
}
}
Tiếp theo, chúng tôi tạo ra một ValueConverter
. Kế thừa từ MarkupExtension
việc sử dụng dễ dàng hơn trong XAML vì vậy chúng tôi không phải khai báo nó là tài nguyên.
[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;
}
}
Tôi ViewModel
chỉ cần 1 thuộc tính mà tôi View
có thể liên kết cho cả SelectedValue
và ItemsSource
của hộp tổ hợp:
private DayOfWeek dayOfWeek;
public DayOfWeek SelectedDay
{
get { return dayOfWeek; }
set
{
if (dayOfWeek != value)
{
dayOfWeek = value;
OnPropertyChanged(nameof(SelectedDay));
}
}
}
Và cuối cùng để ràng buộc ComboBox
chế độ xem (sử dụng ValueConverter
trong ItemsSource
liên kết) ...
<ComboBox ItemsSource="{Binding Path=SelectedDay, Converter={x:EnumToCollectionConverter}, Mode=OneTime}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=SelectedDay}" />
Để thực hiện giải pháp này, bạn chỉ cần sao chép EnumHelper
lớp và lớp của tôi EnumToCollectionConverter
. Họ sẽ làm việc với bất kỳ enum. Ngoài ra, tôi không bao gồm nó ở đây, nhưng ValueDescription
lớp chỉ là một lớp đơn giản với 2 thuộc tính đối tượng công khai, một được gọi Value
, một được gọi Description
. Bạn có thể tự tạo hoặc bạn có thể thay đổi mã để sử dụng một Tuple<object, object>
hoặcKeyValuePair<object, object>