James Coyle
James Coyle

Reputation: 82

WPF TreeView Style DataTemplate binding issues

I'm attempting to setup "expand on select" functionality on a TreeViewItem of a specific type as below:

<UserControl x:Class="ImprovedDirectory.Views.DirectoryView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:ImprovedDirectory.Views"
         xmlns:vms="clr-namespace:ImprovedDirectory.ViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
        <DataTemplate x:Key="CollapsedItem" DataType="{x:Type vms:DirectoryItemViewModel}">
            <TextBlock 
                Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
                VerticalAlignment="Center"
                Margin="5"/>
        </DataTemplate>
        <DataTemplate x:Key="ExpandedItem" DataType="{x:Type vms:DirectoryItemViewModel}">
            <StackPanel DataContext="{Binding}">
                <TextBlock 
                    Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
                    VerticalAlignment="Center"
                    Margin="5"
                    Grid.Row="0"/>
                <TextBlock
                    Text="{Binding Number, UpdateSourceTrigger=PropertyChanged}"
                    VerticalAlignment="Center"
                    Margin="2.5"
                    Grid.Row="1"/>
                <TextBlock
                    Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}"
                    VerticalAlignment="Center"
                    Margin="2.5"
                    Grid.Row="2"/>
            </StackPanel>
        </DataTemplate>
</UserControl.Resources>
<TreeView x:Name="DirectoryTreeView" ItemsSource="{Binding Groups}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type vms:DirectoryGroupViewModel}" ItemsSource="{Binding Items, UpdateSourceTrigger=PropertyChanged}">
            <TextBlock 
                Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
                VerticalAlignment="Center"
                Margin="5"/>
        </HierarchicalDataTemplate>
        <Style TargetType="{x:Type TreeViewItem}">
            <Style.Setters>
                <Setter Property="ItemTemplate" Value="{StaticResource CollapsedItem}"/>
            </Style.Setters>
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="ItemTemplate" Value="{StaticResource ExpandedItem}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </TreeView.Resources>
</TreeView>

The output from above is as follows and I have no idea why: Output example 1

This is what it should look like when nothing is selected: Output example 2

I know it's something really simple I'm missing with the bindings. However, I'm really not sure at this point, I've tested each DataTemplate element in the UserControl.Resources individually and they both work as intended.

I also am having an issue it would seem with the Style overwriting the formatting for the Group as well as just the item, so I guess the second question would be around how I can define which specific DataType is being made primary output for the items, rather than all objects.

Any help would be greatly appreciated.

Upvotes: 0

Views: 535

Answers (1)

thatguy
thatguy

Reputation: 22089

The output that you see comes from the fact that you set a DataTemplate as ItemTemplate in your TreeViewItem style, which has no representation of hierarchy, e.g. an ItemsSource for children.

If you change the templates to be HierarchicalDataTemplates like in your TreeView, you get the hierarchy, but I do not think that this behaves as you intended. As you expand an item and select it or another subsequently, you will see the template changing for all children and selection is broken.

Instead of swapping data templates, you can achieve the desired result in a simpler way by just combining both templates into one and reacting on the IsSelected property in a trigger, that changes the visibility of controls that are hidden when unselected.

<TreeView x:Name="DirectoryTreeView"
          ItemsSource="{Binding Groups}">
   <TreeView.ItemTemplate>
      <HierarchicalDataTemplate DataType="{x:Type vms:DirectoryGroupViewModel}" 
                                ItemsSource="{Binding Items}">
         <StackPanel>
            <TextBlock Text="{Binding Name}"
                       VerticalAlignment="Center"
                       Margin="5"/>
            <TextBlock x:Name="NumberTextBlock"
                       Text="{Binding Number}"
                       VerticalAlignment="Center"
                       Margin="2.5"
                       Visibility="Collapsed"/>
            <TextBlock x:Name="EmailTextBlock"
                       Text="{Binding Email}"
                       VerticalAlignment="Center"
                       Margin="2.5"
                       Visibility="Collapsed"/>
         </StackPanel>
         <HierarchicalDataTemplate.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}, Path=IsSelected}" Value="True">
               <Setter TargetName="NumberTextBlock" Property="Visibility" Value="Visible"/>
               <Setter TargetName="EmailTextBlock" Property="Visibility" Value="Visible"/>
            </DataTrigger>
         </HierarchicalDataTemplate.Triggers>
      </HierarchicalDataTemplate>
   </TreeView.ItemTemplate>
</TreeView>

Instead of triggers, you could also use a BooleanToVisibilityConverter to bind Visibility directly.

<TreeView x:Name="DirectoryTreeView"
          ItemsSource="{Binding Groups}">
   <TreeView.Resources>
      <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
   </TreeView.Resources>
   <TreeView.ItemTemplate>
      <HierarchicalDataTemplate DataType="{x:Type vms:DirectoryGroupViewModel}" 
                                ItemsSource="{Binding Items}">
         <StackPanel>
            <TextBlock Text="{Binding Name}"
                       VerticalAlignment="Center"
                       Margin="5"/>
            <TextBlock Text="{Binding Number}"
                       VerticalAlignment="Center"
                       Margin="2.5"
                       Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}"/>
            <TextBlock Text="{Binding Email}"
                       VerticalAlignment="Center"
                       Margin="2.5"
                       Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}, Path=IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}"/>
         </StackPanel>
      </HierarchicalDataTemplate>
   </TreeView.ItemTemplate>
</TreeView>

Upvotes: 1

Related Questions