【问题标题】:Is it possible to create a generic Int-to-Enum Converter?是否可以创建一个通用的 Int-to-Enum 转换器?
【发布时间】:2011-10-29 22:36:14
【问题描述】:

我想说

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

如果int 值等于(int)MyEnum.Value,则将其解析为True

我知道我可以创建一个返回 (MyEnum)intValueConverter,但是我必须为我在 DataTriggers 中使用的每个 Enum 类型创建一个转换器。

有没有一种通用的方法来创建一个可以给我这种功能的转换器?

【问题讨论】:

    标签: c# wpf enums converter datatrigger


    【解决方案1】:

    可以以可重用的方式在枚举值及其基础整数类型之间创建一个转换器——也就是说,您不需要为每个枚举类型定义一个新的转换器。已向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);
        }
    }
    

    调用时,此转换器会根据 valuetargetType 值在 int/enum 值之间翻转值的类型。没有硬编码的枚举类型。

    【讨论】:

      【解决方案2】:

      我想我明白了

      我只需将我的 ConverterParameter 而不是 Value 设置为等于我正在寻找的枚举,并评估真/假

      <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】:

        过去我们也想这样做几次,因此我们构建了几个扩展方法(在 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] 属性定义的枚举。

        【讨论】:

          【解决方案4】:

          您也可以反过来,使用自定义标记扩展将枚举转换为 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;
              }
          }
          

          【讨论】:

          • 我以前从未遇到过 MarkupExtensions,谢谢!仍然每天都在学习新东西:)
          • 其实也一样,我刚开始使用它们,它们可以很方便:)
          【解决方案5】:

          您可以对 int 值执行 ToString(),然后将其传递给静态 Enum.Parse 或 Enum.TryParse 方法,该方法采用您关心的枚举类型并返回适当的值。

          这不是一个完美的解决方案,因为它不适用于表示多个枚举值的二进制 ORing 的整数

          【讨论】:

          • 如何在Converter 中获取枚举类型?
          猜你喜欢
          • 2016-02-05
          • 2021-02-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多