使用PLAIN绑定进行隐式转换

时间:2014-11-20 11:56:39

标签: c# wpf casting

我正在尝试在WPF中创建一个隐式转换。

让我们说我们有一个枚举:

public enum MyEnum
{
    A1,
    B2,
    C3,
    D5
}

我想将ComboBox中显示的值与不同的东西交换。所以我创建了一个包装类:

public class EnumDisplay
{
    public object Value { get; set; }
    public String Text { get; set; }
}

我用一些实例填充一个集合,比如

new EnumDispay
{
    Value = MyEnum.A1,
    Text = "Foo"
}

我们通常会为这样的ComboBox创建绑定:

<ComboBox ItemsSource="{Binding WhatEver}"
          SelectedValuePath="Value"
          SelectedValue="{Binding Val}"/>

像魅力一样工作 - 但我想使用简化/直接前向绑定。 无转换器,无显示/值路径。就像这样简单的绑定:

<ComboBox ItemsSource="{Binding WhatEver}"
          SelectedItem="{Binding Val}"/>

我尝试在TypeConverter

中添加EnumDispay属性
[TypeConverter(typeof(EnumDisplayTypeConverter))]

但它似乎只用于将我的EnumDisplay类转换为字符串...

public class EnumDisplayTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        //never called
        return base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        //desttype is always String
        if (context != null)
        {
            if (context.Instance != null)
            {
                if (context.Instance.GetType() == typeof(EnumDisplay))
                {
                    return true;
                }
            }
        }
        return false;
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        //never called
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        //desttype is always String
        var t = value as EnumDisplay;
        if (t != null)
        {
            return t.Value.ToString();
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

仍然,我继续接收通常的WPF转换异常:

System.Windows.Data Error: 23 : Cannot convert 'bbb' from type 'EnumDisplay' to type MyEnum' with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException:...
   bei System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
   bei System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   bei System.ComponentModel.EnumConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   bei MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)'
System.Windows.Data Error: 7 : ConvertBack cannot convert value 'bbb' (type 'EnumDisplay'). BindingExpression:Path=Val; DataItem='MainVM' (HashCode=30659444); target element is 'ComboBox' (Name=''); target property is 'SelectedItem' (type 'Object') NotSupportedException:'System.NotSupportedException: EnumConverter...
   bei MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)
   bei MS.Internal.Data.ObjectTargetConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture)
   bei System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'

似乎仍然使用默认EnumConverter。我在这里缺少什么?

更新

我创建了一个简单的转换器来将枚举转换为EnumDisplay:

public class SomeTypeConverter : TypeConverter
{
    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is EnumDisplay)
        {
            return (value as EnumDisplay).Value;
        }
        return base.ConvertFrom(context, culture, value);
    }
}

由于WPF使用TypeDescriptor进行转换器查找,我所要做的就是:

TypeDescriptor.AddAttributes(typeof(MyEnum), new TypeConverterAttribute(typeof(SomeTypeConverter)));

它在一个方向上完美无缺。

但如果我这样做:

ViewModel.Val = MyEnum.D5

ComboBox似乎没有选定的值(已实施ChangeNotification)。另外提供SelectedValuePath属性将解决这个问题,但这是我想要避免的!

2 个答案:

答案 0 :(得分:1)

您应该在枚举类型中应用[TypeConverter(...)]属性,并实施CanConvertFromConvertFrom。对于EnumDisplay,您可以覆盖ToString或使用实施TypeConverterCanConvertTo的{​​{1}}。

当WPF尝试显示ConvertTo课程的实例时,它会检查EnumDisplay是否有EnumDisplay。如果是,则会使用它将TypeConverter - 如果可能的话转换为EnumDisplay,否则转换为UIElement。如果转换为string是可能的并且您已覆盖string,那么它会调用它,否则转换将通过ToString的{​​{1}}进行路由。此处仅使用EnumDisplayTypeConverter

当绑定系统尝试更新绑定的source属性时,它会检查该属性的类型是否具有CanConvertTo。换句话说,如果ConvertToTypeConverter。如果是,则会在其上调用MyEnum。它可能会或可能不会调用TypeConverter,可能取决于您使用的WPF版本。只要实施两者都是安全的。

答案 1 :(得分:0)

即使它没有隐式转换您的ENUM

,您也可以使用以下EnumExtensions让思考更轻松
public static string GetDescription(this Enum value)
{
    Type type = value.GetType();
    string name = Enum.GetName(type, value);
    if (name != null)
    {
        FieldInfo field = type.GetField(name);
        if (field != null)
        {
            var attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (attr != null)
            {
                return attr.Description;
            }
        }
    }
    return value.ToString();
}

public static Dictionary<T, string> EnumToDictionary<T>()
{
    var enumType = typeof(T);

    if (!enumType.IsEnum)
        throw new ArgumentException("T must be of type System.Enum");

    return Enum.GetValues(enumType)
               .Cast<T>()
               .ToDictionary(k => k, v => ( v as Enum ).GetDescription());
}

<强>枚举

using System.ComponentModel;

public enum MyEnum
{
    [Description("foo1")]
    A1,
    [Description("foo2")]
    B2,
    [Description("foo3")]
    C3,
    [Description("foo4")]
    D5
}

<强> ViewModel.cs

public Dictionary<MyEnum, string> Dict
{
    get { return EnumExtension.EnumToDictionary<MyEnum>(); }
}

private MyEnum _selectedValue;

public MyEnum SelectedValue
{
    get { return _selectedValue; }
    set
    {
        _selectedValue= value;

        RaisePropertyChanged(() => Reg(() => SelectedValue));
    }
}

<强> View.xaml

    <ComboBox DisplayMemberPath="Value"
              SelectedValuePath="Key"
              ItemsSource="{Binding Dict}"
               SelectedValue="{Binding SelectedValue}"/>