问题是我有这个枚举,但我不希望组合框显示枚举的值。这是枚举:
public enum Mode
{
[Description("Display active only")]
Active,
[Description("Display selected only")]
Selected,
[Description("Display active and selected")]
ActiveAndSelected
}
所以在ComboBox中而不是显示Active,Selected或ActiveAndSelected,我想为枚举的每个值显示DescriptionProperty。我有一个名为GetDescription()的扩展方法,用于枚举:
public static string GetDescription(this Enum enumObj)
{
FieldInfo fieldInfo =
enumObj.GetType().GetField(enumObj.ToString());
object[] attribArray = fieldInfo.GetCustomAttributes(false);
if (attribArray.Length == 0)
{
return enumObj.ToString();
}
else
{
DescriptionAttribute attrib =
attribArray[0] as DescriptionAttribute;
return attrib.Description;
}
}
那么有没有办法可以将枚举绑定到ComboBox并使用GetDescription扩展方法显示它的内容?
谢谢!
答案 0 :(得分:19)
我建议使用DataTemplate和ValueConverter。这将允许您自定义它的显示方式,但您仍然可以读取组合框的SelectedItem属性并获取实际的枚举值。
ValueConverters需要很多样板代码,但这里没有什么太复杂的。首先,创建ValueConverter类:
public class ModeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return ((Mode) value).GetDescription();
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotSupportedException();
}
}
由于您只是将枚举值转换为字符串(用于显示),因此您不需要ConvertBack - 这仅适用于双向绑定方案。
然后将ValueConverter的实例放入资源中,如下所示:
<Window ... xmlns:WpfApplication1="clr-namespace:WpfApplication1">
<Window.Resources>
<WpfApplication1:ModeConverter x:Key="modeConverter"/>
</Window.Resources>
....
</Window>
然后你准备给ComboBox一个DisplayTemplate,它使用ModeConverter格式化它的项目:
<ComboBox Name="comboBox" ...>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource modeConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
为了测试这个,我也扔了一个Label,它会显示实际的SelectedItem值,它确实显示了SelectedItem是enum而不是显示文本,这就是我想要的:
<Label Content="{Binding ElementName=comboBox, Path=SelectedItem}"/>
答案 1 :(得分:7)
这就是我用MVVM做的事情。在我的模型上,我会定义我的枚举:
public enum VelocityUnitOfMeasure
{
[Description("Miles per Hour")]
MilesPerHour,
[Description("Kilometers per Hour")]
KilometersPerHour
}
在我的ViewModel上,我公开了一个属性,它提供了可能的选择作为字符串以及一个属性来获取/设置模型的值。如果我们不想在类型中使用每个枚举值,这很有用:
//UI Helper
public IEnumerable<string> VelocityUnitOfMeasureSelections
{
get
{
var units = new []
{
VelocityUnitOfMeasure.MilesPerHour.Description(),
VelocityUnitOfMeasure.KilometersPerHour.Description()
};
return units;
}
}
//VM property
public VelocityUnitOfMeasure UnitOfMeasure
{
get { return model.UnitOfMeasure; }
set { model.UnitOfMeasure = value; }
}
此外,我使用通用的EnumDescriptionCoverter:
public class EnumDescriptionConverter : IValueConverter
{
//From Binding Source
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Enum)) throw new ArgumentException("Value is not an Enum");
return (value as Enum).Description();
}
//From Binding Target
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is string)) throw new ArgumentException("Value is not a string");
foreach(var item in Enum.GetValues(targetType))
{
var asString = (item as Enum).Description();
if (asString == (string) value)
{
return item;
}
}
throw new ArgumentException("Unable to match string to Enum description");
}
}
最后,根据我的观点,我可以做到以下几点:
<Window.Resources>
<ValueConverters:EnumDescriptionConverter x:Key="enumDescriptionConverter" />
</Window.Resources>
...
<ComboBox SelectedItem="{Binding UnitOfMeasure, Converter={StaticResource enumDescriptionConverter}}"
ItemsSource="{Binding VelocityUnitOfMeasureSelections, Mode=OneWay}" />
答案 2 :(得分:6)
我喜欢你的想法。但GetCustomAttributes使用反射。这对你的表现有什么影响?
看看这篇文章: WPF - 在ComboBox控件中显示枚举 http://www.infosysblogs.com/microsoft/2008/09/wpf_displaying_enums_in_combob.html
答案 3 :(得分:3)
除了使用反射和属性的问题之外,有几种方法可以做到这一点,但我认为最好的方法是创建一个包含枚举值的小视图模型类:
public class ModeViewModel : ViewModel
{
private readonly Mode _mode;
public ModeViewModel(Mode mode)
{
...
}
public Mode Mode
{
get { ... }
}
public string Description
{
get { return _mode.GetDescription(); }
}
}
或者,您可以考虑使用ObjectDataProvider
。
答案 4 :(得分:3)
我建议您使用我已经发布here的标记扩展,只需稍加修改:
[MarkupExtensionReturnType(typeof(IEnumerable))]
public class EnumValuesExtension : MarkupExtension
{
public EnumValuesExtension()
{
}
public EnumValuesExtension(Type enumType)
{
this.EnumType = enumType;
}
[ConstructorArgument("enumType")]
public Type EnumType { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (this.EnumType == null)
throw new ArgumentException("The enum type is not set");
return Enum.GetValues(this.EnumType).Select(o => GetDescription(o));
}
}
然后你可以这样使用它:
<ComboBox ItemsSource="{local:EnumValues local:Mode}"/>
编辑:我建议的方法将绑定到字符串列表,这是不可取的,因为我们希望SelectedItem是Mode类型。最好删除.Select(...)部分,并在ItemTemplate中使用自定义转换器的绑定。
答案 5 :(得分:0)
我这样做了:
<ComboBox x:Name="CurrencyCodeComboBox" Grid.Column="4" DisplayMemberPath="." HorizontalAlignment="Left" Height="22" Margin="11,6.2,0,10.2" VerticalAlignment="Center" Width="81" Grid.Row="1" SelectedValue="{Binding currencyCode}" >
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
代码中的我设置了itemSsource:
CurrencyCodeComboBox.ItemsSource = [Enum].GetValues(GetType(currencyCode))