在你使用wpf应用程序开发的时候,是否需要进行数据绑定到Enum数据呢?在这篇文章中,我将向你展示在WPF中处理Enum数据绑定的方法。
假设存在一个这样的Enum数据的定义,具体内容如下文代码中所示:
namespace LocalizeFrameworkWpfApp { public enum Status { Horrible, Bad, SoSo, Good, Better, Best } } 一、WPF中的通常处理方法 1.1 添加引用在MainWindow.xaml文件中从mscorlib中引入命名空间System。
xmlns:sys="clr-namespace:System;assembly=mscorlib" 1.2 创建一个ObjectDataProvider资源在此步骤中,你需要创建一个ObjectDataProvider的资源,并给它一个键名x:Key="DataFromEnum",这样就可以使用DataFromEnum在代码中使用它。并且你需要给MethodName设置为Enum类型上存在的GetValues,然后将ObjectType设置为Enum类型。接下来,你将需设置ObjectDataProvider.MethodParameters的Enum类型。最后,你添加的ObjectDataProvider资源如下面代码所示
<Window.Resources> <ObjectDataProvider x:Key="DataFromEnum" MethodName="GetValues" ObjectType="{x:Type sys:Enum}"> <ObjectDataProvider.MethodParameters> <x:Type TypeName="local:Status"> </x:Type> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> </Window.Resources> 1.3 Binding数据处理现在,你可以使用数据绑定了。例如,想将数据绑定到ComboBox上面,那么你需要设置ItemSource为一个新的绑定,并将数据源绑定到我们上面定义的名为DataFromEnum的资源。
<Grid> <ComboBox MinWidth="150" HorizontalAlignment="Center" VerticalAlignment="Center" ItemsSource="{Binding Source={StaticResource DataFromEnum}}"> </ComboBox> </Grid>到现在为止,所有的已经处理完成,运行程序可以看到数据已经正确绑定到ComboBox上面。
让我们来看看当数据绑定Enum类型时,如何使用WPF特性来改进代码的使用和可读性。首先,想封装Enum类型的绑定而不需要ObjectDataProvider资源的逻辑处理,还希望不需要必须定义资源才能在xaml中使用绑定功能。理想情况下,应该像处理普通对象的绑定一样,将所有内容都内联处理。为此,需要利用定制MarkupExtension的帮助类。这个扩展将简单的接受Enum类型,然后为控件创建一个可绑定Enum值的列表,这种实现其实很简单。
2.1 MarkupExtension帮助类MarkupExtension帮助类定义如下:
namespace LocalizeFrameworkWpfApp { public class EnumBindingSourceExtension:MarkupExtension { private Type _enumType; public Type EnumType { get { return _enumType; } set { if (value != _enumType) { if (null != value) { var enumType = Nullable.GetUnderlyingType(value) ?? value; if (!enumType.IsEnum) { throw new ArgumentException("Type must bu for an Enum"); } } _enumType = value; } } } public EnumBindingSourceExtension() { } public EnumBindingSourceExtension(Type enumType) { EnumType = enumType; } public override object ProvideValue(IServiceProvider serviceProvider) { if (null == _enumType) { throw new InvalidOperationException("The EnumTYpe must be specified."); } var actualEnumType = Nullable.GetUnderlyingType(_enumType) ?? _enumType; var enumValues = Enum.GetValues(actualEnumType); if (actualEnumType == _enumType) { return enumValues; } var tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1); enumValues.CopyTo(tempArray, 1); return tempArray; } } } 2.2 Binding数据处理 <Grid> <StackPanel> <ComboBox MinWidth="150" HorizontalAlignment="Center" ItemsSource="{Binding Source={StaticResource DataFromEnum}}"> </ComboBox> <ComboBox MinWidth="150" HorizontalAlignment="Center" ItemsSource="{Binding Source={local:EnumBindingSource {x:Type local:Status}}}"> </ComboBox> </StackPanel> </Grid>看一下运行结果:
三、扩展:添加Enum类型的描述(Description)支持现在我们可以不用使用ObjectDataProvider资源进行Enum类型的绑定工作了。这两种方法进行对比一下,详细这个新方法会让你耳目一新,像发现了新大陆一般。
而Enum类型的值一般使用在程序中,而为了让用户获得更好的使用体验,一般都会在枚举值前面添加上属性:Description描述。为了完成此工作,我们只需使用TypeConverter进行转换。
namespace LocalizeFrameworkWpfApp { public class EnumDescriptionTypeConverter:EnumConverter { public EnumDescriptionTypeConverter(Type type) : base(type) { } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string)) { if (null != value) { FieldInfo fi = value.GetType().GetField(value.ToString()); if (null != fi) { var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false); return ((attributes.Length > 0) && (!string.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : value.ToString(); } } return string.Empty; } return base.ConvertTo(context, culture, value, destinationType); } } }