Search code examples
.netwpftreeviewhierarchical-datatreeviewitem

Changing the template of a TreeViewItem when it is selected


I'm having some problems changing the DataTemplate that is used for a TreeViewItem when it is selected. Ideally, I would like each item to contain a TextBlock, and then when selected it should contain a TextBox instead.

Here is what I have so far (I used this question as a starting point):

<Window>
    <Window.Resources>
        <HierarchicalDataTemplate x:Key="normal"
            ItemsSource="{Binding Path=Children}">
            <TextBlock Text="{Binding Path=Text}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="selected"
            ItemsSource="{Binding Path=Children}">
            <TextBox Text="{Binding Path=Text}" />
        </HierarchicalDataTemplate>
        <Style TargetType="{x:Type TreeViewItem}" x:Key="ContainerStyle">
            <Setter Property="ItemTemplate" Value="{StaticResource normal}" />
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="ItemTemplate" Value="{StaticResource selected}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resource>
    <Grid>
        <TreeView ItemSource="{Binding Body}" ItemContainerStyle="{StaticResource ContainerStyle}" />
    </Grid>
</Window>

What happens is that there is only one node in the tree, and the text of the node is the type name of the object. It sounds like the type bound to the node isn't what the template is expecting, so it's using the default ToString() binding instead of the Text property as I specified.

I have set the DataContext of the Window in the code behind file. I know that my Bindings for the data are correct, because if I set one HierarchicalDataTemplate for the TreeView the data is displayed correctly.

I think that my problem is that I need to set a property other than ItemTemplate in the TreeViewItem styles - am I using the right property, or should I set something else?


Solution

  • It's actually the HeaderTemplate you need - that's what governs the style of the node itself. Just so there is a complete sample, this is what worked for me:

    <Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <HierarchicalDataTemplate x:Key="normal"
                                 ItemsSource="{Binding Path=Children}">
            <TextBlock Text="{Binding Path=Text}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate x:Key="selected"
                                 ItemsSource="{Binding Path=Children}">
            <TextBox Text="{Binding Path=Text}" />
        </HierarchicalDataTemplate>
        <Style TargetType="{x:Type TreeViewItem}"
               x:Key="ContainerStyle">
            <Setter Property="HeaderTemplate"
                    Value="{StaticResource normal}" />
            <Style.Triggers>
                <Trigger Property="IsSelected"
                         Value="True">
                    <Setter Property="HeaderTemplate"
                            Value="{StaticResource selected}" />
                </Trigger>
            </Style.Triggers>
        </Style>
        </Window.Resources>
        <Grid>
            <TreeView x:Name="_Tree" ItemContainerStyle="{StaticResource ContainerStyle}"/>
        </Grid>
    </Window>
    

    .. with some test code behind like this:

    Imports System.Collections.ObjectModel
    
    Class Window1
    
        Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    
            Dim Root As New Node
            Root.Text = "Root"
    
            Dim Child As New Node
            Child.Text = "Child"
            Root.Children.Add(Child)
    
            Dim Nodes As New Collection(Of Node)
            Nodes.Add(Root)
            _tree.itemssource = Nodes
    
        End Sub
    
    End Class
    
    Public Class Node
    
        Private _Text As String
        Public Property Text() As String
            Get
                Return _Text
            End Get
            Set(ByVal Value As String)
                _Text = Value
            End Set
        End Property
    
        Private _Children As New Collection(Of Node)
        Public Property Children() As Collection(of node)
            Get
                Return _Children
            End Get
            Set(ByVal Value As Collection(of node))
                _Children = Value
            End Set
        End Property
    
    End Class