在WPF中一种较好的绑定Enums数据方法

在WPF中一种较好的绑定Enums数据方法

引言

在你使用wpf应用程序开发的时候,是否需要进行数据绑定到 Enum 数据呢?在这篇文章中,我将向你展示在WPF中处理 Enum 数据绑定的方法。

假设存在一个这样的 Enum 数据的定义,具体内容如下文代码中所示:

namespace LocalizeFrameworkWpfApp
    public enum Status
        Horrible,
        SoSo,
        Good,
        Better,

一、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; }
                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")]
        [Description("This is SoSo")]
        SoSo,
        [Description("This is Good")]
        Good,
        [Description("This is Better")]
        Better,