可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
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.
Is there a generic way to create a converter that would give me this kind of functionality?
回答1:
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.
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. There are no hard-coded enum types.
回答2:
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
<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; }
回答3:
You could also go the other way around and convert the enum to int for the Value using a custom Markup Extension.
Example
<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; } }
回答4:
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. The core of all of these is implemented in a single static generic TryAsEnum method:
/// <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.
回答5:
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.
This isn't a perfect solution though because it won't work with integers that represent the binary ORing of multiple enum values