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
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) - 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)
- In Silverlight, have to hard-code a fixed set of supported types (in WPF, can use the
Would be happy to hear any other ideas for working around this issue today, or in WPF 4.
Best Answer
In .NET 4, there is an
IServiceProvider
available through which you can getIDestinationTypeProvider
, and then you should be able to do what you need. In .NET 3 or 4, theIProvideValueTarget
service can give you the targetObject and targetProperty. From the targetProperty (aPropertyInfo
,MethodInfo
-for attached, or aDependencyProperty
), you can get the type.To get the
IDestinationTypeProvider
service provider from withinConvertFrom
of aTypeConverter
for example: