Question

I'm new to WPF so may be missing something obvious but haven't been able to find an answer anywhere.

Basically I'm building a custom tree collection that sorts in a particular way and the nodes of the tree are typed to the type of the collection. When testing in a TreeView the HierarchicalDataTemplate doesn't work when the nodes are generic. If they're not generic it's fine.

Here's a simple node class to illustrate:

public class SimpleNode<T>
{
    private List<SimpleNode<T>> _children;
    private string _name;

    public SimpleNode(string name)
    {
        _name = name;
    }

    public List<SimpleNode<T>> Children
    {
        get
        {
            return _children;
        }
        set
        {
            _children = value;
        }
    }

    public string Name
    {
        get
        {
            return _name;
        }
    }
}

then in the xaml:

HierarchicalDataTemplate DataType="{x:Type local:SimpleNode`1}" ItemsSource="{Binding Children}"

Apparently the `1 appended to the type is a result of SimpleNode having 1 generic param. If I strip the class of generics the HierarchicalDataTemplate works and I can view the tree in the TreeView. With the generics in place it won't do it.

Any ideas?

Thanks in advance.

Was it helpful?

Solution

There is no support for that out of the box. But you can make it work by creating your own DataTemplateSelector that supports generics:

public class OpenGenericTypeDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item,
                                                DependencyObject container)
    {
        var element = container as FrameworkElement;
        if (item != null && element != null)
        {
            if (item.GetType().IsGenericType)
            {
                var genericTypeDefinition = item.GetType()
                                                .GetGenericTypeDefinition();
                var key = new DataTemplateKey(genericTypeDefinition);
                return element.TryFindResource(key) as DataTemplate;
            }
        }

        return null;
    }
}

You than can use it like this:

<UserControl.Resources>
  <OpenGenericTypeDataTemplateSelector x:Key="GenericTemplateSelector" />
</UserControl.Resources>
...
<TreeView ItemTemplateSelector="{StaticResource GenericTemplateSelector}" ...>
  <TreeView.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:SimpleNode`1}"
                              ItemsSource="{Binding Children}">
      ...
    </HierarchicalDataTemplate>
  </TreeView.Resources>

What that does is actually very simple:
When you create a DataTemplate for a certain type, that type's name will automatically become the key of that template.
The default DataTemplateSelector uses the type of the item as lookup value. But the type of a an instance of a generic class is a closed generic type, i.e. one that has the generic type parameter specified.
Problem is: The template has been registered for the open generic type, i.e. without the generic type parameter specified. That's why the default DataTemplateSelector doesn't pick up DataTemplates registered for an open generic type.
The implementation I provided adds support for open generic types by simply checking the type of the item. If it is generic, we get ourselves the open generic type (via GetGenericTypeDefinition) and use that as the key.

To actually make use of this selector, we have to tell the TreeView that it should use it. We are doing that by specifying the ItemTemplateSelector.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top