在WPF中一种较好的绑定Enums数据方法
引言
在你使用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);
}
}
}
然后对定义的枚举值添加上[Description]
属性
namespace LocalizeFrameworkWpfApp
{
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
public enum Status
{
[Description("This is horrible")]
Horrible,
[Description("This is Bad")]
Bad,
[Description("This is SoSo")]
SoSo,
[Description("This is Good")]
Good,
[Description("This is Better")]
Better,
[Description("This is Best")]
Best
}
}
程序运行结果:
可以看到,我们添加了[Description]
属性时,这两种方法都可以将[Description]
属性的值绑定到指定控件中。