绑定ComboBoxes枚举…在Silverlight中!

所以,网页和StackOverflow,有很多很好的答案,如何将一个combobox绑定到WPF中的枚举属性。 但是Silverlight缺less所有使这个可能的function:(例如:

  1. 您不能使用接受types参数的genericsEnumDisplayer风格的IValueConverter ,因为Silverlight不支持x:Type
  2. 你不能使用ObjectDataProvider ,就像这种方法一样 ,因为它不存在于Silverlight中。
  3. 由于标记扩展在Silverlight中不存在,所以不能像#2链接的注释那样使用自定义标记扩展。
  4. 因为XAML不支持generics(并且使得它们的工作都依赖于标记扩展,Silverlight不支持),所以不能使用generics的#1版本。

大规模的失败!

正如我所看到的,做这项工作的唯一方法是要么

  1. 作弊和绑定到我的ViewModel中的string属性,其setter / getter进行转换,使用View中的代码隐藏将值加载到ComboBox中。
  2. 为每个我想要绑定的枚举创build一个自定义的IValueConverter

有没有更通用的替代scheme,即不要一遍又一遍地写我想要的每一个枚举? 我想我可以做解决scheme#2使用接受enum作为types参数的generics类,然后创build新的类为每个我想要的枚举

 class MyEnumConverter : GenericEnumConverter<MyEnum> {} 

你的想法是什么?

唉,我说话太快了! 至less在Silverlight 3中有一个非常好的解决scheme 。(它可能只有3个,因为这个线程表示在Silverlight 3中修正了与这个东西有关的bug。)

基本上,对于ItemsSource属性,您只需要一个转换器,但是只要您传递一个types为MyEnum的属性的名称,它就可以是完全通用的,而不使用任何禁用的方法。 数据绑定SelectedItem是完全无痛的; 不需要转换器! 那么,至less只要你不想为每个枚举值定制string通过例如DescriptionAttribute ,HMM …可能需要另一个转换器, 希望我能把它变成通用的。

更新:我做了一个转换器,它的工作原理! 我不得不现在绑定到SelectedIndex ,不过没关系。 使用这些家伙:

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Data; namespace DomenicDenicola.Wpf { public class EnumToIntConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // Note: as pointed out by Martin in the comments on this answer, this line // depends on the enum values being sequentially ordered from 0 onward, // since combobox indices are done that way. A more general solution would // probably look up where in the GetValues array our value variable // appears, then return that index. return (int)value; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return Enum.Parse(targetType, value.ToString(), true); } } public class EnumToIEnumerableConverter : IValueConverter { private Dictionary<Type, List<object>> cache = new Dictionary<Type, List<object>>(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var type = value.GetType(); if (!this.cache.ContainsKey(type)) { var fields = type.GetFields().Where(field => field.IsLiteral); var values = new List<object>(); foreach (var field in fields) { DescriptionAttribute[] a = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false); if (a != null && a.Length > 0) { values.Add(a[0].Description); } else { values.Add(field.GetValue(value)); } } this.cache[type] = values; } return this.cache[type]; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } 

用这种绑定的XAML:

 <ComboBox x:Name="MonsterGroupRole" ItemsSource="{Binding MonsterGroupRole, Mode=OneTime, Converter={StaticResource EnumToIEnumerableConverter}}" SelectedIndex="{Binding MonsterGroupRole, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}" /> 

而这种资源声明XAML:

 <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ddwpf="clr-namespace:DomenicDenicola.Wpf"> <Application.Resources> <ddwpf:EnumToIEnumerableConverter x:Key="EnumToIEnumerableConverter" /> <ddwpf:EnumToIntConverter x:Key="EnumToIntConverter" /> </Application.Resources> </Application> 

任何意见将不胜感激,因为我有点XAML / Silverlight / WPF /等。 新手。 例如, EnumToIntConverter.ConvertBack会慢一点,所以我应该考虑使用caching?

还有另一种方法可以将ComboBox绑定到枚举,而不需要为所选项目定制转换器。 你可以检查一下

http://charlass.wordpress.com/2009/07/29/binding-enums-to-a-combobbox-in-silverlight/

它不使用DescriptionAttributes ….但它对我来说是完美的,所以我想这取决于它将被使用的场景

我发现枚举数据的简单封装更容易使用。

 public ReadOnly property MonsterGroupRole as list(of string) get return [Enum].GetNames(GetType(GroupRoleEnum)).Tolist End get End Property private _monsterEnum as GroupRoleEnum Public Property MonsterGroupRoleValue as Integer get return _monsterEnum End get set(value as integer) _monsterEnum=value End set End Property 

 <ComboBox x:Name="MonsterGroupRole" ItemsSource="{Binding MonsterGroupRole, Mode=OneTime}" SelectedIndex="{Binding MonsterGroupRoleValue , Mode=TwoWay}" /> 

这将完全消除转换器的需要… 🙂

这是Windows 8.1 / Windows Phone通用应用程序的相同设置,主要变化是: –

  • 在框架中缺lessDescriptionAttribute(或者至less我找不到)
  • reflection如何工作的差异(使用TypeInfo.Declared字段)

看来,XAML的顺序也很重要,我不得不把ItemsSource放在SelectedIndex之前,否则它不会调用ItemsSource绑定。

 <ComboBox ItemsSource="{Binding Path=MyProperty,Mode=OneWay, Converter={StaticResource EnumToIEnumerableConverter}}" SelectedIndex="{Binding Path=MyProperty, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}" /> 

下面的代码

 namespace MyApp.Converters { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Windows.UI.Xaml.Data; public class EnumToIntConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { // Note: as pointed out by Martin in the comments on this answer, this line // depends on the enum values being sequentially ordered from 0 onward, // since combobox indices are done that way. A more general solution would // probably look up where in the GetValues array our value variable // appears, then return that index. return (int) value; } public object ConvertBack(object value, Type targetType, object parameter, string language) { return value; } } public class EnumToIEnumerableConverter : IValueConverter { private readonly Dictionary<TypeInfo, List<object>> _cache = new Dictionary<TypeInfo, List<object>>(); public object Convert(object value, Type targetType, object parameter, string language) { var type = value.GetType().GetTypeInfo(); if (!_cache.ContainsKey(type)) { var fields = type.DeclaredFields.Where(field => field.IsLiteral); var values = new List<object>(); foreach (var field in fields) { var a = (DescriptionAttribute[]) field.GetCustomAttributes(typeof(DescriptionAttribute), false); if (a != null && a.Length > 0) { values.Add(a[0].Description); } else { values.Add(field.GetValue(value)); } } _cache[type] = values; } return _cache[type]; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } [AttributeUsage(AttributeTargets.Field)] public class DescriptionAttribute : Attribute { public string Description { get; private set; } public DescriptionAttribute(string description) { Description = description; } } }