How to reference a specific implementation of a generic type in the DataType attribute of a DataTemplate?
This question is strongly connected to this answer to "How to reference a generic type in the DataType attribute of a HierarchicalDataTemplate?"
I followed the basic idea of that answer and created this data structure:
<!-- for DictItemVM<string, Remote.Address> which is a viewmodel for a KeyValuePair<...> -->
<x:Array Type="{x:Type sys:Type}"
x:Key="KVParamsStringToRemoteAddress"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:remote="clr-namespace:Remote"
xmlns:mvvm="clr-namespace:MVVM">
<x:Type TypeName="sys:String" />
<mvvm:GenericType BaseType="{x:Type TypeName=remote:Address}"/>
</x:Array>
<mvvm:GenericType xmlns:mvvm="clr-namespace:MVVM"
BaseType="{x:Type TypeName=mvvm:DictItemVM`2}"
InnerTypes="{StaticResource KVParamsStringToRemoteAddress}"
x:Key="DictItemVMOfStringToRemoteAddress"/>
DictItemVM<T,U>
is a viewmodel for a KeyValuePair<...>
and is derived from BaseVM. BaseVM has a DataTemplate view, but I'm trying hard to create one for DictItemVM<string, Remote.Address>
.
<DataTemplate x:Key="TestKey" DataType="{StaticResource DictItemVMOfStringToRemoteAddress}">
<StackPanel>
<Label Content="UniqueName" />
<TextBox Text="{Binding UniqueName}" />
<Label Content="Key"/>
<TextBox Text="{Binding Key, Mode=OneWay}" IsEnabled="False" />
<Label Content="Value"/>
<ContentControl Content="{Binding Value, Mode=OneWay}" />
</StackPanel>
</DataTemplate>
Now this DataTemplate should be used as a view, but instead the view for BaseVM is being displayed.
Someone give me a hint on this one?[edit: 2010-08-09]
Some things I tried:In the x:Array definition I replaced
<mvvm:GenericType BaseType="{x:Type TypeName=remote:Address}"/>
with
<x:Type TypeName="remote:Address"/>
,
because that's what it basically is - no difference.
Also tried to create the DataType in between tags (instead of linking to a StaticResource) like this:
<DataTemplate x:Key="TestKey">
<DataTemplate.DataType>
<Binding>
<Binding.Source>
<mvvm:GenericType
BaseType="{x:Type TypeName=mvvm:DictItemVM`2}">
<mvvm:GenericType.InnerTypes>
<x:Type TypeName="sys:String" />
<x:Type TypeName="remote:Address"/>
</mvvm:GenericType.InnerTypes>
</mvvm:GenericType>
</Binding.Source>
</Binding>
</DataTemplate.DataType>
Tried it with and without an x:Array within the GenericType.InnerTypes, both giving me this error.
Tried to pass the type from a static property like this:
DataType="{x:Static mvvm:StaticTypes.DictItemVMOfStringToRemoteAddress}"
and like this:
DataType="{Binding Path={x:Static mvvm:StaticTypes.DictItemVMOfStringToRemoteAddress}}"
No difference.
Strange enough this specific DataTemplate needs to have some x:Key
value, in contrast to all others in the xaml resource file which all point to a regular type like e.g.: DataType="{x:Type mvvm:EffectVM}"
. If I remove the x:Key, I get this error.
I found a solution, though that solution is not really satisfying.
In XAML, create a DataTemplate for each type of KeyValuePair<T,U>
you want to display and give it some unique x:Key:
<DataTemplate x:Key="DictItemOfStringAndAddressVM">
<!-- ... -->
</DataTemplate>
Then in codebehind, create a DataTemplateSelector and override SelectTemplate:
public class GenericDataTemplateSelector : System.Windows.Controls.DataTemplateSelector
{
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if ((element != null) && (item != null))
{
if (item is DictItemVM<string, Remote.Address>)
{
return element.FindResource("DictItemOfStringAndAddressVM") as DataTemplate;
}
else if(item is SomeOtherComplexType)
{
// ...
}
else return base.SelectTemplate(item, container);
}
return null;
}
}
Again in XAML, declare this class as a resource:
<mvvm:GenericDataTemplateSelector x:Key="GenDataTempSelect"/>
Finally, (in my case) in the ContentControl, add the property:
ContentTemplateSelector="{StaticResource GenDataTempSelect}"
--
Disadvantages:
- When creating a new DataTemplate you have to change code at two locations.
- Each ContentControl, ListView, ... must set it's appropriate property.
- Doesn't really answer the question of how to reference generic types in WPF!
Advantages:
- Easy to add new types of any structure or complexity (enjoying all the benefits C# has over WPF...)
- No complicated nested type description in WPF, as the above solution would require.
精彩评论