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:
- 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 implementsIConvertible.ToType
, or has aTypeConvert
that canConvertTo
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
- WPF ignores custom type descriptors, and
Here are the alternatives I have come up with for "working around" this issue:
- 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)
- In Silverlight, have to hard-code a fixed set of supported types (in WPF, can use the
- 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)
- Silverlight does not support custom markup extensions (it also does not support the
- 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.