TypeConverter не работает с ExpandoObject и DataGrid

У меня есть класс Poco, в котором одно свойство является типом перечисления.
Чтобы сделать свойство перечисления локализуемым на уровне пользовательского интерфейса (WPF), я создал TypeConverter для типа перечисления, который ищет локализованное значение перечисления. Список экземпляров этого класса POCO привязан к WPF DataGrid:

// in the ViewModel:
public Pocos = CollectionViewSource.GetDefaultView( DataLayer.GetPocos() );

Это сработало отлично.

Позже я переработал код, чтобы слой данных больше не возвращал List<Poco>, а List<ExpandoObject>:

public Pocos = CollectionViewSource.GetDefaultView( DataLayer.GetExpandos() );

Эти ExpandoObjects являются результатом запроса к базе данных и содержат только подмножество исходных Poco's свойств, в зависимости от оператора select.

Все работает нормально, за исключением TypeConverter для свойства перечисления, которое больше не вызывается.

TypeConverter все еще открыт, так что это работает:

dynamic eo = new ExpandoObject();
eo.EnumProperty = MyEnum.SomeValue;
var converter = TypeConverter.GetConverter( (eo.EnumProperty).GetType() );
var result = converter.ConvertTo( eo.EnumProperty, typeof( string ) );

Я не знаю, что происходит внутри DataGrid WPF. У меня сложилось впечатление, что он по какой-то причине просто вызывает ToString() для свойства типа перечисления вместо вызова TypeConverter.

Любой способ заставить это работать?

Детали реализации:

XAML:

<DataGrid x:Name="PocoGrid" x:FieldModifier="public"  
  Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2"
  ItemsSource="{Binding Pocos}" 
  SelectedItem="{Binding SelectedPoco}"
  bh:DataGridColumnsBehavior.BindableColumns="{Binding PocoColumns}"
  IsSynchronizedWithCurrentItem="True" IsReadOnly="True" AutoGenerateColumns="False">
  <DataGrid.InputBindings>
    <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding PocoDoubleClick}" CommandParameter="{Binding Pocos/}"/>
  </DataGrid.InputBindings>
</DataGrid>

ПРЕОБРАЗОВАТЕЛЬ ТИПА:

public abstract class EnumTypeConverterBase : TypeConverter
{
   public static ILocalizedResourceProvider ResourceProvider { get; set; } = null;
}

public class EnumTypeConverter<T> : EnumTypeConverterBase where T : struct, IConvertible, IComparable
{
   private static string typeParameterName = typeof(T).Name;

   public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType )
   {
      if( sourceType == typeof( string ) )
         return true;
      return base.CanConvertFrom( context, sourceType );
   }

   public override object ConvertFrom( ITypeDescriptorContext context, CultureInfo culture, object value )
   {
      throw new NotImplementedException();
   }

   public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType )
   {
      string val = value.ToString();

      if( ResourceProvider == null )
         return val;
      else
         return ResourceProvider.GetEnum( typeParameterName, val );
   }
}

ОПРЕДЕЛЕНИЕ ПЕРЕЧИСЛЕНИЯ:

[TypeConverter( typeof( EnumTypeConverter<MyEnum> ) )]
public enum MyEnum
{
   Unknown = 0,
   SomeValue = 1,
   SomeOtherValue = 2
}

person naitsirhc    schedule 07.06.2017    source источник


Ответы (1)


Использовать преобразователь значений< /а>. Во всяком случае, это более традиционный способ сделать это, и он не зависит от рефлексии для вызова во время выполнения.

public class MyEnumLocalizationConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var e = (MyEnum)value;

        var converted = "";

        //  Do whatever the type converter now does

        return converted;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

XAML:

<Window.Resources>
    <!-- ... -->

    <local:MyEnumLocalizationConverter x:Key="MyEnumLocalizer" />

    <!-- ... -->
</Window.Resources>

<!-- ... -->

    <DataGridTextColumn
        Binding="{Binding EnumProperty, Converter={StaticResource MyEnumLocalizer}}"
        />

Если вы поделитесь XAML, я могу показать вам

person 15ee8f99-57ff-4f92-890c-b56153    schedule 07.06.2017
comment
Я добавил некоторые детали реализации, включая XAML. Я не уверен, подойдет ли ValueConvert для моего случая. Реализация содержит множество перечислений, а преобразователь значений, по-видимому, относится к одному типу. Можно ли определить общую версию? - person naitsirhc; 07.06.2017
comment
@c__k Вы можете сохранить преобразователи типов и написать преобразователь общего значения, который использует отражение для получения правильного преобразователя типов. Я думаю, что это суть того, что XAML делал для вас раньше с преобразователями типов, когда у него было типизированное свойство в качестве источника привязки. - person 15ee8f99-57ff-4f92-890c-b56153; 07.06.2017
comment
Я пытался заставить это работать в течение нескольких часов, но в конце концов сдался. Проблема заключалась в том, что мне пришлось бы привязывать преобразователь ко всем столбцам, потому что столбец и его имя, в котором может встречаться тип перечисления, неизвестны во время компиляции. В итоге я создал тип во время выполнения и использовал его вместо ExpandoObject. - person naitsirhc; 08.06.2017
comment
@c__k Верно, я не думал, что это так далеко. Создание типа во время выполнения! Вот это да. - person 15ee8f99-57ff-4f92-890c-b56153; 08.06.2017