简体   繁体   English

是否可以创建一个通用的Int-to-Enum转换器?

[英]Is it possible to create a generic Int-to-Enum Converter?

I'd like to be able to say 我想能说

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

and to have it resolve as True if the int value is equal to (int)MyEnum.Value 如果int值等于(int)MyEnum.Value其解析为True

I know I could make a Converter that returns (MyEnum)intValue , however then I'd have to make a converter for every Enum type I use in my DataTriggers. 我知道我可以创建一个返回(MyEnum)intValueConverter ,但是我必须为我在DataTriggers中使用的每个Enum类型创建一个转换器。

Is there a generic way to create a converter that would give me this kind of functionality? 有没有一种通用的方法来创建一个能够提供这种功能的转换器?

It is possible to create a converter between enum values and their underlying integral types in a reusable way -- that is, you don't need to define a new converter for each enum types. 可以以可重用的方式在枚举值及其底层整数类型之间创建转换器 - 也就是说,您不需要为每个枚举类型定义新的转换器。 There's enough information provided to Convert and ConvertBack for this. 有提供足够的信息ConvertConvertBack这一点。

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);
    }
}

When invoked, this converter flips the value's type between int/enum value based purely on the value and targetType values. 调用时,此转换器仅基于valuetargetType值在int / enum值之间翻转值的类型。 There are no hard-coded enum types. 没有硬编码的枚举类型。

I think I figured it out 我想我明白了

I just needed to set my ConverterParameter instead of the Value equal to the Enum I am looking for, and evaluate for True/False 我只需要设置我的ConverterParameter而不是Value等于我正在寻找的Enum,并评估为True / False

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

Converter 变流器

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;
}

You could also go the other way around and convert the enum to int for the Value using a custom Markup Extension. 你也可以反过来使用自定义标记扩展将枚举转换为int的int。

Example

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

EnumToIntExtension 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;
    }
}

We've wanted to do this a few times in the past as well, so we built several extension methods (on int, long, etc) to help us out. 我们过去也想过几次这样做,所以我们构建了几个扩展方法(在int,long等)来帮助我们。 The core of all of these is implemented in a single static generic TryAsEnum method: 所有这些的核心都是在一个静态通用的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;
    }

This implementation handles Enums with any underlying type, as well as enums defined with the [Flags] attribute. 此实现处理具有任何基础类型的枚举,以及使用[Flags]属性定义的枚举。

You could do a ToString() on the int value and then pass that into the static Enum.Parse or Enum.TryParse method which takes the enum type you care about and returns the appropriate value. 您可以对int值执行ToString(),然后将其传递给静态Enum.Parse或Enum.TryParse方法,该方法采用您关注的枚举类型并返回适当的值。

This isn't a perfect solution though because it won't work with integers that represent the binary ORing of multiple enum values 这不是一个完美的解决方案,因为它不适用于表示多个枚举值的二进制ORing的整数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM