Можно ли создать универсальный конвертер Int-to-Enum? - PullRequest
9 голосов
/ 18 августа 2011

Я бы хотел сказать

<DataTrigger Binding="{Binding SomeIntValue}" 
             Value="{x:Static local:MyEnum.SomeValue}">

и разрешить его как True, если значение int равно (int)MyEnum.Value

Я знаю, что мог бы сделать Converter, который возвращает (MyEnum)intValue, однако тогда мне пришлось бы сделать конвертер для каждого типа Enum, который я использую в своих DataTriggers.

Есть ли общий способ создания конвертера, который дал бы мне такую ​​функциональность?

Ответы [ 5 ]

9 голосов
/ 09 ноября 2015

Можно создать преобразователь между значениями перечисления и их базовыми целочисленными типами многоразовым способом, то есть вам не нужно определять новый преобразователь для каждого типа перечисления. Для этого достаточно информации Convert и ConvertBack.

public sealed class BidirectionalEnumAndNumberConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return null;

        if (targetType.IsEnum)
        {
            // convert int to enum
            return Enum.ToObject(targetType, value);
        }

        if (value.GetType().IsEnum)
        {
            // convert enum to int
            return System.Convert.ChangeType(
                value,
                Enum.GetUnderlyingType(value.GetType()));
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // perform the same conversion in both directions
        return Convert(value, targetType, parameter, culture);
    }
}

При вызове этот преобразователь отображает тип значения между значением int / enum, основываясь исключительно на значениях value и targetType. Нет жестко закодированных типов перечислений.

4 голосов
/ 18 августа 2011

Я думаю, я понял это

Мне просто нужно было установить ConverterParameter вместо Value равным искомому Enum, и оценить для True / False

<DataTrigger Value="True"
             Binding="{Binding SomeIntValue, 
                 Converter={StaticResource IsIntEqualEnumConverter},
                 ConverterParameter={x:Static local:MyEnum.SomeValue}}">

Конвертер

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    if (parameter == null || value == null) return false;

    if (parameter.GetType().IsEnum && value is int)
    {
        return (int)parameter == (int)value;
    } 
    return false;
}
3 голосов
/ 18 августа 2011

Вы также можете пойти другим путем и преобразовать enum в int для значения, используя специальное расширение разметки.

Пример

<DataTrigger Binding="{Binding Path=MyNumber}"
             Value="{Markup:EnumToInt {x:Static Visibility.Visible}}">

EnumToIntExtension

public class EnumToIntExtension : MarkupExtension
{
    public object EnumValue
    {
        get;
        set;
    }
    public EnumToIntExtension(object enumValue)
    {
        this.EnumValue = enumValue;
    } 
    public override object ProvideValue(IServiceProvider provider)
    {
        if (EnumValue != null && EnumValue is Enum)
        {
            return System.Convert.ToInt32(EnumValue);
        }
        return -1;
    }
}
1 голос
/ 18 августа 2011

Мы хотели сделать это несколько раз в прошлом, поэтому мы создали несколько методов расширения (для int, long и т. Д.), Чтобы выручить нас.Ядро всего этого реализовано в одном статическом универсальном методе TryAsEnum:

    /// <summary>
    /// Helper method to try to convert a value to an enumeration value.
    /// 
    /// If <paramref name="value"/> is not convertable to <typeparam name="TEnum"/>, an exception will be thrown
    /// as documented by Convert.ChangeType.
    /// </summary>
    /// <param name="value">The value to convert to the enumeration type.</param>
    /// <param name="outEnum">The enumeration type value.</param>
    /// <returns>true if value was successfully converted; false otherwise.</returns>
    /// <exception cref="InvalidOperationException">Thrown if <typeparamref name="TEnum"/> is not an enum type. (Because we can't specify a generic constraint that T is an Enum.)</exception>
    public static bool TryAsEnum<TValue, TEnum>( TValue value, out TEnum outEnum ) where TEnum : struct
    {
        var enumType = typeof( TEnum );

        if ( !enumType.IsEnum )
        {
            throw new InvalidOperationException( string.Format( "{0} is not an enum type.", enumType.Name ) );
        }

        var valueAsUnderlyingType = Convert.ChangeType( value, Enum.GetUnderlyingType( enumType ) );

        if ( Enum.IsDefined( enumType, valueAsUnderlyingType ) )
        {
            outEnum = (TEnum) Enum.ToObject( enumType, valueAsUnderlyingType );
            return true;
        }

        // IsDefined returns false if the value is multiple composed flags, so detect and handle that case

        if( enumType.GetCustomAttributes( typeof( FlagsAttribute ), inherit: true ).Any() )
        {
            // Flags attribute set on the enum. Get the enum value.
            var enumValue = (TEnum)Enum.ToObject( enumType, valueAsUnderlyingType );

            // If a value outside the actual enum range is set, then ToString will result in a numeric representation (rather than a string one).
            // So if a number CANNOT be parsed from the ToString result, we know that only defined values have been set.
            decimal parseResult;
            if( !decimal.TryParse( enumValue.ToString(), out parseResult ) )
            {
                outEnum = enumValue;
                return true;
            }
        }

        outEnum = default( TEnum );
        return false;
    }

Эта реализация обрабатывает перечисления с любым базовым типом, а также перечисления, определенные с атрибутом [Flags].

0 голосов
/ 18 августа 2011

Вы можете сделать ToString () для значения int, а затем передать его в статический метод Enum.Parse или Enum.TryParse, который принимает тип перечисления, который вам нужен, и возвращает соответствующее значение.

ThisЭто не идеальное решение, потому что оно не будет работать с целыми числами, представляющими двоичное ORing нескольких значений enum

...