问题描述:

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>.

Remote.Address is a complex value type (stores Path and Access information). Remote.Address has its own DataTemplate view.

So now that I have the StaticResource "DictItemVMOfStringToRemoteAddress", I want to use it to specify a DataTemplate:

<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.
相关阅读:
Top