3

TypeConverter for generic type used in xaml

 1 year ago
source link: https://stackoverflow.com/questions/1723263/typeconverter-for-generic-type-used-in-xaml/1724958#1724958
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

TypeConverter for generic type used in xaml

I am looking into initializing members of generic types declared in XAML. This is targeting the (reduced) generics support in WPF 4 and future Silverlight. (I have tried the scenarios below using x:TypeArguments and XamlReader.Load in VS2010 Beta 2, but will use TestClassInt32 : TestClass<int> { } for simplicity, as it has the same behavior as using the generic type directly, but is easier to test using compiled xaml today.)


Here are the test types I am using.

public class TestClass<T> {
  [TypeConverter( typeof(StringListToItemsConverter) )]
  public IEnumerable<T> Items { get; set; }
  public TestProperty<T> Property { get; set; }
}

[TypeConverter( typeof(TestPropertyConverter) )]
public struct TestProperty<T> {
  public TestProperty( T value ) : this() { Value = value; }
  public T Value { get; }
}

Here is the example scenario.

<StackPanel>
  <StackPanel.DataContext>
    <test:TestClassInt32 Items="1,2,3" Property="6" />
  </StackPanel.DataContext>

  <TextBox Text="{Binding Property.Value}" />
  <ItemsControl ItemsSource="{Binding Items}" />
</StackPanel>

When I hard-code typeof(int) into the converters for this example, everything works fine, but that approach obviously does not work for TestClass<double> or TestClass<DateTime>. The problem is that the TypeConverter.ConvertFrom method does not have access to the destination type, only the source type. (This was not a problem when TypeConverter was created in .NET 1.0, because the destination type could not be parameterized, but is an unfortunate limitation now.)


Here are the approaches I have looked at to get around this problem:

  1. Make the type converter generic; e.g. [TypeConverter( typeof(TestPropertyConverter<T>) )]

    • .NET does not support type parameters in attributes
    • Have the TypeConverter return an intermediate type that either implements IConvertible.ToType, or has a TypeConvert that can ConvertTo the destination type
    • XAML parser only does one-step conversions: if the returned object cannot be assigned to the destination, it throws an exception
    • Define a custom type descriptor that will return the appropriate converter based on the actual type
    • WPF ignores custom type descriptors, and TypeDescriptor does not even exist in Silverlight

Here are the alternatives I have come up with for "working around" this issue:

  1. Require the destination type to be embedded in the string; e.g. "sys:Double 1,2,3"

    • In Silverlight, have to hard-code a fixed set of supported types (in WPF, can use the IXamlTypeResolver interface to get the actual type that "sys:Double" corresponds to)
    • Write a custom markup extension that takes a type parameter; e.g. "{List Type={x:Type sys:Double}, Values=1,2,3}"
    • Silverlight does not support custom markup extensions (it also does not support the x:Type markup extension, but you could use the hard-coded approach from option 1)
    • Create a wrapper type that takes a type parameter and re-defines all of the generic members as object, forwarding all member accesses to the underlying strongly-typed object
    • Possible, but makes for a very poor user experience (have to cast to get underlying generic object, have to still use hard-coded list for type parameter on Silverlight, have to cache member assignments until the type argument is assigned, etc, etc; generally loses most of the benefits of strong typing with generics)

Would be happy to hear any other ideas for working around this issue today, or in WPF 4.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK