C# 带纯绑定的Impicit转换

C# 带纯绑定的Impicit转换,c#,wpf,casting,C#,Wpf,Casting,我试图在WPF中创建隐式强制转换 假设我们有一个枚举: public enum MyEnum { A1, B2, C3, D5 } 我想用不同的东西交换组合框中显示的值。因此,我创建了一个包装器类: public class EnumDisplay { public object Value { get; set; } public String Text { get; set; } } 我使用以下实例填充集合: new EnumDispay

我试图在WPF中创建隐式强制转换

假设我们有一个枚举:

public enum MyEnum
{
    A1,
    B2,
    C3,
    D5
}
我想用不同的东西交换组合框中显示的值。因此,我创建了一个包装器类:

public class EnumDisplay
{
    public object Value { get; set; }
    public String Text { get; set; }
}
我使用以下实例填充集合:

new EnumDispay
{
    Value = MyEnum.A1,
    Text = "Foo"
}
我们通常为这样的组合框创建绑定:

<ComboBox ItemsSource="{Binding WhatEver}"
          SelectedValuePath="Value"
          SelectedValue="{Binding Val}"/>
<ComboBox ItemsSource="{Binding WhatEver}"
          SelectedItem="{Binding Val}"/>
ViewModel.Val = MyEnum.D5
但它似乎只用于将我的
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
。我错过了什么

更新 我创建了一个简单的转换器,用于将枚举强制转换为枚举显示:

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)));
它在一个方向上工作得非常好

但如果我这样做:

<ComboBox ItemsSource="{Binding WhatEver}"
          SelectedValuePath="Value"
          SelectedValue="{Binding Val}"/>
<ComboBox ItemsSource="{Binding WhatEver}"
          SelectedItem="{Binding Val}"/>
ViewModel.Val = MyEnum.D5

组合框似乎不再具有所选值(实现了ChangeNotification)。同时提供SelectedValuePath属性将解决这个问题,但这是我想要避免的

您应该对枚举类型应用
[TypeConverter(…)]
属性,并实现
CanConvertFrom
ConvertFrom
。对于
EnumDisplay
,您可以覆盖
ToString
,或者使用实现
CanConvertTo
ConvertTo
TypeConverter

当WPF试图显示
EnumDisplay
类的实例时,它将检查
EnumDisplay
是否有
TypeConverter
。如果是,它将使用它将
枚举显示
-如果可能,转换为
ui元素
,否则转换为
字符串
。如果可以转换为
字符串
,并且您已经覆盖了
到字符串
,那么它将调用该字符串,否则转换将通过
枚举显示
类型转换器
进行。此处仅使用
CanConvertTo
ConvertTo


但是,当绑定系统尝试更新绑定的源属性时,它会检查该属性的类型是否具有
TypeConverter
。换句话说,如果
MyEnum
有一个
TypeConverter
。如果它这样做了,它将对其调用
ConvertFrom
。它可能会调用,也可能不会调用
CanConvertFrom
,这可能取决于您使用的WPF版本。为了安全起见,只需实现这两者。

您可以使用以下EnumExtensions使思考更容易,即使它不会隐式强制转换您的ENUM

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());
}
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));
    }
}
公共字典Dict
{
获取{返回EnumExtension.EnumToDictionary();}
}
私有MyEnum _selectedValue;
公共MyEnum SelectedValue
{
获取{return\u selectedValue;}
设置
{
_selectedValue=值;
RaisePropertyChanged(()=>Reg(()=>SelectedValue));
}
}
View.xaml

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


我猜覆盖EnumDisplay的ToString()不是一个选项?欢迎来到StackOverflow!请看,其中的共识是“不,他们不应该”。@KroaX这不允许将枚举绑定到项本身。当这些属性正是针对这种情况时,您将很难避免使用
SelectedValue[Path]
。我认为这对于那些将来维护您的代码的人来说是不值得的。使用这个
EnumDisplay
类,您想要实现什么?如果您只想在直接绑定原始枚举值的同时更改显示文本,那么可以通过编程为每个枚举类型生成一个
DataTemplate
,并在运行时将其注入应用程序的资源。(不检查是否可以进行转换)为什么会有
CanConvertFrom
,它有什么合法性。。。根据:重写此方法以提供您自己的转换要求。。。?是的,有一个
CanConvertFrom
方法,但WPF似乎没有使用它。我怀疑,在
CanConvertFrom
中尝试并返回
false
后,现在绑定不再工作了…:/奇怪,那样做对我没有任何影响。我想这取决于WPF版本1使用的是什么?无论如何,我会更新我的答案。在这里运行.NET4。。。你的版本是什么?