I have a ViewModel defined like this:
public class LocationTreeViewModel :
ObservableCollection, INotifyPropertyChanged
In XAML 2006 this is not supported. You can, however, roll your own if you want to have this functionality.
This link has a nice tutorial on creating markup extensions.
Usage would be like this:
<Grid xmlns:ext="clr-namespace:CustomMarkupExtensions">
<TextBlock Text="{ext:GenericType FooLocationTreeViewModel(Of Foo)}" />
</Grid>
You have to choose and implement the syntax though. I suggest the VB notation since it won't interfere like the C# notation does with < and >.
The {x:Type} markup extension supports allows generic type arguments to be specified as a comma separated list in parentheses.
Here's an example:
<UserControl x:Class="Test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<UserControl.Resources>
<DataTemplate DataType="{x:Type generic:List(sys:Int64)}">
<TextBlock Text="{Binding Count}"/>
</DataTemplate>
</UserControl.Resources>
</UserControl>
I am using .Net 4.5 on VS 2015, so your mileage may vary.
I know, that I'm a little late to the party, but I want post an answer for all those who may see this question in the future:
It is possible.
You can see the whole code in the answer to this question: DataTemplates and Generics. But since it is quite long, I will just copy the important bits. If you want more details, then look into the referenced question.
You need to write a MarkupExtension
which can provide a closed generic type.
public class GenericType : MarkupExtension
{
public GenericType() { }
public GenericType(Type baseType, params Type[] innerTypes)
{
BaseType = baseType;
InnerTypes = innerTypes;
}
public Type BaseType { get; set; }
public Type[] InnerTypes { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
Type result = BaseType.MakeGenericType(InnerTypes);
return result;
}
}
Now you can define your type which closes your generic type in xaml, and then use the closed generic type as DataType
of an DataTemplate
.
<Window.Resources>
<x:Array Type="{x:Type System:Type}"
x:Key="ListWithTwoStringTypes">
<x:Type TypeName="System:String" />
<x:Type TypeName="System:String" />
</x:Array>
<WpfApp1:GenericType BaseType="{x:Type TypeName=Generic:Dictionary`2}"
InnerTypes="{StaticResource ListWithTwoStringTypes}"
x:Key="DictionaryStringString" />
<DataTemplate DataType="{StaticResource DictionaryStringString}">
<TextBlock Text="Hi Dictionary"
FontSize="40"
Foreground="Cyan"/>
</DataTemplate>
</Window.Resources>
Be happy that the defined DataTemplate
gets automatically selected by WPF.
The only way i could do this is to use MarkupExtensions
.
public class GenericType : MarkupExtension
{
private readonly Type _of;
public GenericType(Type of)
{
_of = of;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return typeof(LocationTreeViewModel<>).MakeGenericType(_of);
}
}
And to use it i just need to do this:
<DataTemplate DataType="{app:GenericType app:TreeBaseClass}">
Late and not exactly the answer to the question (CollinE and Bas already sayed the it is actually not possible)... However, maybe the alternativ solution may be helpful for others:
It is possible to resolve generic types by using a TemplateSelector like that:
TemplateSelector
public class MyTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var genericType = typeof(MyGenericType<>);
var isMyGeneric = item?.GetType().GetGenericTypeDefinition() == genericType;
return isMyGeneric ? MyTemplate : OtherTemplate;
}
public DataTemplate MyTemplate { get; set; }
public DataTemplate OtherTemplate { get; set; }
}
XAML
<UserControl.Resources>
<DataTemplate x:Key="MyTemplate">
<!-- Set Up MyTemplate -->
</DataTemplate>
<DataTemplate x:Key="OtherTemplate">
<!-- Set Up OtherTemplate -->
</DataTemplate>
<local:MyTemplateSelector x:Key="MyTemplateSelector"
MyTemplate="{StaticResource MyTemplate}"
OtherTemplate="{StaticResource MyTemplate}" />
</UserControl.Resources>
...
<ContentControl ContentTemplateSelector="{StaticResource MyTemplateSelector}"
Content="{Binding ViewModel}" />
Slightly improved version of MarkupExtension, work for classes upto 3 generic parameters.
public class GenericTypeExtension : MarkupExtension
{
public GenericTypeExtension()
{
}
public GenericTypeExtension(string baseTypeName_, Type genericType1_, Type genericType2_, Type genericType3_)
{
BaseTypeName = baseTypeName_;
GenericType1 = genericType1_;
GenericType2 = genericType2_;
GenericType3 = genericType3_;
}
public string BaseTypeName { get; set; }
public string BaseTypeAssemblyName { get; set; }
public Type GenericType1 { get; set; }
public Type GenericType2 { get; set; }
public Type GenericType3 { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider_)
{
var list = new List<Type>();
if (GenericType1 != null)
{
list.Add(GenericType1);
}
if (GenericType2 != null)
{
list.Add(GenericType2);
}
if (GenericType3 != null)
{
list.Add(GenericType3);
}
var type = Type.GetType(string.Format("{0}`{1}, {2}", BaseTypeName, list.Count, BaseTypeAssemblyName));
if (type != null)
{
return type.MakeGenericType(list.ToArray());
}
return null;
}
}