1

我有一个基于 C# MVVM Light 的应用程序,它有几个枚举属性。我厌倦了编写管道代码来支持它们。通常,我编写的管道代码是成对字符串属性的形式,或者有时是类型特定的值转换器,以促进数据绑定到枚举属性。我怎样才能做到这一点,而无需编写额外的代码来将数据绑定的 UI 元素连接到枚举属性?

4

1 回答 1

1

我做了一些挖掘和试验,现在我有一个用于任何 ViewModel 枚举属性的值转换器,它在枚举值的描述​​属性字符串和它们表示的枚举常量之间进行双向转换。这允许您将双向数据绑定到枚举属性,而无需执行任何管道代码。您唯一需要做的就是将绑定属性的 Enumeration 类型的完全限定类型名称放在ConverterParameter字段中(示例见下图)。如果您在确定正确的完全限定的 Enum 类型名称时遇到问题,只需在ConvertBack()中设置一个断点,将抛出异常。然后打电话System.Reflection.Assembly.GetExecutingAssembly().DefinedTypes.ToList()以获取该执行上下文中所有当前定义的系统类型的列表。找到正确的完全限定类型名称并将其粘贴到ConverterParameter字段中。

  • 使用ToDescriptionsList<>()方便地从Enum类型中获取 Description 属性以填充列表框或其他元素。将对它的调用放在属性中,该属性将人类友好的字符串列表返回到绑定到枚举属性的 UI 元素。(例如,列表框的ItemsSource属性)。

具有描述属性的枚举类型示例:

    // (barnyard, bird, cat, dog, horse, pig, reptile, smallfurry)
    // List of Animal types the breed list method accepts.
    public enum EnumAnimalType
    {
        [Description("Barnyard")]
        barnyard,
        [Description("Birds")]
        bird,
        [Description("Cats & Kittens")]
        cat,
        [Description("Dogs & Puppies")]
        dog,
        [Description("Horses & Ponies")]
        horse,
        [Description("Pigs")]
        pig,
        [Description("Reptiles")]
        reptile,
        [Description("Other Small & Furry")]
        smallfurry
    }

数据绑定期间的示例值转换器声明

// Value converter class that does the conversion work.
public class EnumToDescAttrConverter : IValueConverter
{
    // Derived Grant Barrintgon's blog on C#.

    /// <summary>
    /// Extension method that retrieves the description attribute for a particular enum value.
    /// [Description("Bright Pink")]
    /// BrightPink = 2,
    /// </summary>
    /// <param name="en">The Enumeration</param>
    /// <returns>A string representing the friendly name</returns>
    public string GetDescription(Enum en)
    {
        Type type = en.GetType();

        MemberInfo[] memInfo = type.GetMember(en.ToString());

        if (memInfo != null && memInfo.Length > 0)
        {
            object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs != null && attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }

        // Unable to find a description attribute for the enum.  Just return the
        //  value of the ToString() method.
        return en.ToString();
    }

    // Consumer wants to convert an enum to a description attribute string.
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Since we don't know what the correct default value should be, a NULL value is unacceptable.
        if (value == null)
            throw new ArgumentNullException("(EnumToDescAttrConverter:Convert) The value is unassigned.");

        Enum e = (Enum)value;

        return e.GetDescription();
    } // public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

    // Convert an enumeration value in Description attribute form back to the appropriate enum value.
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Since we don't know what the correct default value should be, a NULL value is unacceptable.
        if (value == null)
            throw new ArgumentNullException("(EnumToDescAttrConverter:ConvertBack) The value is unassigned.");

        string strValue = (string)value;

        // Parameter parameter must be set since it must contain the concrete Enum class name.
        if (parameter == null)
            throw new ArgumentNullException("(EnumToDescAttrConverter:ConvertBack) The Parameter parameter is unassigned.");

        string theEnumClassName = parameter.ToString();

        // Create an instance of the concrete enumeration class from the given class name.
        Enum e = (Enum)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(theEnumClassName);

        if (e == null)
            throw new ArgumentException(
                "(EnumToDescAttrConverter:ConvertBack) Invalid enumeration class name: " + theEnumClassName
                + ". Set a break point here and call System.Reflection.Assembly.GetExecutingAssembly().DefinedTypes.ToList()"
                + " in the immediate window to find the right type.  Put that type into the Converter parameter for the"
                + " data bound element you are working with."
                );

        System.Type theEnumType = e.GetType();

        Enum eRet = null;

        foreach (MemberInfo memInfo in theEnumType.GetMembers())
        {
            object[] attrs = memInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs != null && attrs.Length > 0)
            {
                if (((DescriptionAttribute)attrs[0]).Description == strValue)
                {
                    // Ignore the case
                    eRet = (Enum)Enum.Parse(theEnumType, memInfo.Name, true);
                    break; // Found it.
                }
            }
        } // foreach (MemberInfo memInfo in typeof(TEnum).GetMembers())

        // If the string can not be converted to a valid enum value, throw an
        //  Exception.
        if (eRet == null)
            throw new ArgumentException(String.Format("{0} can not be converted to an enum value: ", strValue));

        return eRet;
    } // public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

    /// <summary>
    ///  Returns all the values for given Enum as a list of their string attributes.  <br />
    ///   Use this method to fill a list box with human friendly strings for each <br />
    ///   enumeration value using the DescriptionAttribute() associated it/them.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>An enumerator for the Enum values</returns>
    public static List<string> ToDescriptionsList<T>()
    {
        // GetValues() is not available on Windows Phone.
        // return Enum.GetValues(typeof(T)).Cast<T>();
        List<string> listRet = new List<string>();

        foreach (var x in typeof(T).GetFields())
        {
            Enum e;

            if (x.IsLiteral)
            {
                e = (Enum)x.GetValue(typeof(Enum));

                listRet.Add(e.GetDescription());
            } // if (x.IsLiteral)
        } // foreach()

        return listRet;
    } // public static IEnumerable<T> GetValues<T>(this T theEnum)    } // public class EnumToDescAttrConverter : IValueConverter
} // public class EnumToDescAttrConverter : IValueConverter
于 2013-04-21T02:21:51.950 回答