【发布时间】:2016-03-09 11:46:58
【问题描述】:
我想在我的 ViewModel 上有一个枚举,假设它代表一个人的性别。代表该 ViewModel 的 View 应该能够提供一种提供该值的方式;无论是一组单选按钮还是一个组合框(如果有很多)。并且有很多示例,您可以在 XAML 中对单选按钮进行硬编码,每个按钮都说明它代表的值。更好的也会使用显示属性的名称来为单选按钮提供文本。
我希望更进一步。我希望它根据 Enum 的值以及 DisplayAttribute 的名称和描述等动态生成 RadioButtons。理想情况下,如果它超过 6 个项目(可能作为某种控件实现),我希望它选择创建一个 ComboBox(而不是 RadioButtons);但是在我们尝试跑步之前,让我们看看我们是否可以走路。 :)
我的谷歌搜索让我非常接近......这就是我所拥有的:
public enum Gender
{
[Display(Name="Gentleman", Description = "Slugs and snails and puppy-dogs' tails")]
Male,
[Display(Name = "Lady", Description = "Sugar and spice and all things nice")]
Female
}
窗口:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:EnumMultiConverter x:Key="EnumMultiConverter"/>
<ObjectDataProvider
MethodName="GetValues"
ObjectType="{x:Type local:EnumDescriptionProvider}"
x:Key="AdvancedGenderTypeEnum">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:Gender"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding Source={StaticResource AdvancedGenderTypeEnum}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="{Binding GroupName}" Content="{Binding Name}" ToolTip="{Binding Description}">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource EnumMultiConverter}" Mode="TwoWay">
<Binding RelativeSource="{RelativeSource AncestorType=ItemsControl}" Path="DataContext.Gender" Mode="TwoWay" />
<Binding Path="Value" Mode="OneWay"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
EnumDescriptionProvider:
public static class EnumDescriptionProvider
{
public static IList<EnumerationItem> GetValues(Type enumType)
{
string typeName = enumType.Name;
var typeList = new List<EnumerationItem>();
foreach (var value in Enum.GetValues(enumType))
{
FieldInfo fieldInfo = enumType.GetField(value.ToString());
var displayAttribute = (DisplayAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(DisplayAttribute));
if (displayAttribute == null)
{
typeList.Add(new EnumerationItem
{
GroupName = typeName,
Value = value,
Name = value.ToString(),
Description = value.ToString()
});
}
else
{
typeList.Add(new EnumerationItem
{
GroupName = typeName,
Value = value,
Name = displayAttribute.Name,
Description = displayAttribute.Description
});
}
}
return typeList;
}
}
枚举项:
public class EnumerationItem
{
public object GroupName { get; set; }
public object Value { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
还有MultiConverter(因为IValueConverter不能为ConverterParameter做一个Binding):
public class EnumMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].Equals(values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
所以我遇到的唯一问题是我无法执行 ConvertBack。但也许有人有一个绝妙的解决方案。正如我所说,理想情况下,我只想要一些可以绑定到 ViewModel 上的枚举的神奇控件,并让它为该枚举的每个值动态创建 RadioButtons。但我会接受我能得到的任何建议。
【问题讨论】:
-
当字典同样能很好地完成任务时,为什么要使用枚举,当我们有编译时间常数时使用枚举。在你的情况下
Dictionary<string,DisplayAttribute>也会这样做 -
打上面我看错问题了
-
为什么要转换回来?您已经有了索引 - 只需将选定的索引绑定到视图模型,您就可以将索引(一个 int)转换为枚举。
-
@code4life 这是双向绑定,所以我需要转换器双向工作。是的,我可以在我的 ViewModel 中添加其他内容,但由于我实现视图的方式,我不应该更改我的视图模型。另外,如果我有 5 个枚举,这意味着我需要添加 5 个额外的属性。公认的解决方案表明 XAML 应该 是多么简单,并且不需要更改视图模型。
标签: c# wpf mvvm data-binding