Search code examples
wpftreeviewhierarchicaldatatemplateitemspanel

Using HierarcicalDataTemplates in conjunction with TreeViewItem control templates


I am having some difficulty figuring out how to template the following TreeView item layout:

TreeView Item Layout Mockup

I have several items, SearchList, which contains a collection of Search, which contains a collection of DataSet (sort of, but that is beside the point). What I am having difficulty with is styling each node level the way I want. I am using MVVM, and the TreeViews ItemsSource property is set to an ObservableCollection of SearchListViewModels which in turn contain my objects all the way down the object tree.

I can successfully style the SearchList HierarchicalDataTemplate to display them correctly. Where I get hung up is on SearchTerm nodes styling. I want the DataSets to be represented in a wrap panel or uniform grid (I haven't decided yet) to the right of the SearchTerm content area. I have modified a TreeViewItem control template to behave this way I think), however if I set it in the ItemContainerStyle property of the Search HierarchicalDataTemplate, it does nothing. All that gets displayed is the content for the Search.

My Altered TreeViewItem Template

<Style TargetType="{x:Type TreeViewItem}" x:Key="AlteredTreeViewItem">
    <Setter Property="HorizontalContentAlignment"
        Value="Stretch" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TreeViewItem}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"
                            MinWidth="19" />
                        <ColumnDefinition Width="0.414*" />
                        <ColumnDefinition Width="0.586*"/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="Bd" HorizontalAlignment="Stretch"
                        Grid.Column="1" Grid.ColumnSpan="1" Background="#7F058956">
                        <ContentPresenter x:Name="PART_Header" Margin="10,0" />
                    </Border>
                    <WrapPanel x:Name="ItemsHost"
                        Grid.Column="2" IsItemsHost="True"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

My Search Hierarchical Data Template

    <HierarchicalDataTemplate DataType="{x:Type local:SearchViewModel}"  ItemsSource="{Binding MySearch.Custodians}" ItemContainerStyle="{StaticResource AlteredTreeViewItem}">
        <TextBlock Text="{Binding MySearch.SearchName}" Foreground="Black" FontFamily="Arial" FontSize="16"/>
    </HierarchicalDataTemplate>

Surely it is possible to both style differently and have child items laid out differently? How can this be achieved?


Solution

  • It seems that you are pretty close to what you're after. I tried to recreate your scenario based on the code you posted and I noted some problems with it (which of course are based on my interpretation of the code you posted)

    • You are missing the ContentSource="Header" part of the ContentPresenter
    • I think you are applying the ItemContainerStyle at the wrong HierarchicalDataTemplate level. It should be specified on the parent in order to affect the children (in your case SearchListViewModel).
    • The default Template for TreeViewItem lays out the ContentPresenter in an Auto sized ColumnDefinition so the WrapPanel won't succesfully wrap unless you modify the ItemContainerStyle for the parent as well. I changed it to a UniformGrid in my sample below

    With the changes from above and a few other things I got a result that looks like this which hopefully is pretty close to what you're after

    enter image description here

    I uploaded the sample solution here: https://www.dropbox.com/s/4v2t8imikkagueb/TreeViewAltered.zip?dl=0

    And here is the Xaml code for it (too much code to post it all..)

    <Window.Resources>
        <!-- DataSet-->
        <HierarchicalDataTemplate DataType="{x:Type data:DataSet}">
            <Border BorderThickness="3"
                    BorderBrush="Gray"
                    Background="Green">
                <TextBlock Text="{Binding Path=Tables[0].TableName}"
                           Margin="5"/>
            </Border>
        </HierarchicalDataTemplate>
    
        <!-- SearchViewModel -->
        <HierarchicalDataTemplate DataType="{x:Type viewModel:SearchViewModel}"
                                  ItemsSource="{Binding DataSets}">
            <TextBlock Text="{Binding DisplayName}"
                       Foreground="Black"
                       FontFamily="Arial"
                       FontSize="16"/>
        </HierarchicalDataTemplate>
    
        <!-- SearchListViewModel -->
        <HierarchicalDataTemplate DataType="{x:Type viewModel:SearchListViewModel}"
                                  ItemsSource="{Binding SearchList}">
            <HierarchicalDataTemplate.ItemContainerStyle>
                <Style TargetType="TreeViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type TreeViewItem}">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" MinWidth="19" />
                                        <ColumnDefinition Width="0.414*" />
                                        <ColumnDefinition Width="0.586*"/>
                                    </Grid.ColumnDefinitions>
                                    <Border x:Name="Bd"
                                            HorizontalAlignment="Stretch" 
                                            Grid.Column="1"
                                            Grid.ColumnSpan="1"
                                            Background="#7F058956">
                                        <ContentPresenter x:Name="PART_Header"
                                                          ContentSource="Header"
                                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                                    </Border>
                                    <UniformGrid x:Name="ItemsHost"
                                                 Grid.Column="2"
                                                 Columns="3"
                                                 IsItemsHost="True"/>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </HierarchicalDataTemplate.ItemContainerStyle>
            <TextBlock Text="{Binding DisplayName}"
                       FontSize="20"/>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <Grid>
        <TreeView ItemsSource="{Binding SearchListViewModels}" />
    </Grid>