Using HierarcicalDataTemplates in Combination with TreeViewItem Control Templates

Good afternoon,

I find it difficult to understand how to create a template for the following TreeView element: TreeView Item Layout Mockup

I have several elements, SearchList, which contains the Search collection, which contains the DataSet collection (sort of, but not point-related). It’s hard for me to style every node level the way I want. I use MVVM, and the TreeViews ItemsSource property is set to the ObservableCollection of SearchListViewModels, which in turn contains my objects all the way down the object tree.

I can successfully create a SearchList HierarchicalDataTemplate hierarchy to display them correctly. Where I hung up on SearchTerm nodes design. I want the DataSets to appear in the wrap panel or unified grid (I haven't decided yet) to the right of the SearchTerm content area. I modified the TreeViewItem control template to behave the way I think), however, if I set it in the ItemContainerStyle property of Search HierarchicalDataTemplate, it does nothing. All that is displayed is searchable content.

Modified Tree Template TreeViewItem

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

Hierarchical Search 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> 

Of course, others had objects that were decorated differently, while children had different things. Does anyone have experience doing this, who can lend me a hand?

+6
source share
2 answers

It seems that you are pretty close to what you need. I tried to recreate your script based on the code you posted, and I noted some problems with it (which, of course, is based on my interpretation of the code you posted)

  • You are missing ContentSource="Header" part of ContentPresenter
  • I think you are applying ItemContainerStyle to the wrong HierarchicalDataTemplate level. It must be specified on the parent to affect children (in your case, SearchListViewModel ).
  • By default, the Template for TreeViewItem displays a ContentPresenter of Auto size of ColumnDefinition , so the WrapPanel will not complete successfully unless you change the ItemContainerStyle for the parent. I changed it to UniformGrid in my example below

With the changes above and a few other things, I got a result that looks like we hope is pretty close to what you after

enter image description here

I downloaded 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 publish all this.)

 <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> 
+5
source

Something that I learned a long time ago when I was trying to create a similar interface was that you are better using ListBox than TreeView .

Why?

  • If you have only one extension level (as you can see from your example), you will have much more control over the layout, since you have one DataTemplate style.

  • It is much easier to set up a ListBox than a TreeView , since you are not related to the GridViewColumnHeader and GridViewColumnPresenters , etc.

To get part of the extension (which is why you originally selected TreeView ), simply use a Grid with two rows, and Expander in the second row, associated with the IsChecked a ToggleButton property. See the Example I pulled from my Log Viewer.

 <DataTemplate> <Grid Margin="0,0,0,3" Grid.IsSharedSizeScope="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="30" SharedSizeGroup="SSG_TimeIcon"/> <ColumnDefinition Width="120" SharedSizeGroup="SSG_Time"/> <ColumnDefinition Width="30" SharedSizeGroup="SSG_LevelIcon"/> <ColumnDefinition Width="70" SharedSizeGroup="SSG_Level"/> <ColumnDefinition Width="*" SharedSizeGroup="SSG_Message"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- ProgramTime --> <Rectangle Grid.Column="0" Grid.Row="0" Margin="0,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{StaticResource Icon_Timer}"/> <TextBlock Grid.Column="1" Grid.Row="0" Margin="5,0,0,0" VerticalAlignme="Top" HorizoalAlignme="Stretch" Text="{Binding Path=TimeStamp, Converter={StaticResource ObjectToStringConverter}}" ToolTip="{Binding Path=ProgramTime}"/> <!-- Level --> <Rectangle Grid.Column="2" Grid.Row="0" Margin="10,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{Binding Path=Level, Converter={StaticResource MappingConverterNinjaLogLevelEnumToBrushResource}}"/> <TextBlock Grid.Column="3" Grid.Row="0" Margin="5,0,0,0" Text="{Binding Path=LevelFriendlyName}" VerticalAlignme="Top" HorizoalAlignme="Stretch"/> <!-- Message --> <StackPanel Grid.Column="4" Grid.Row="0" Margin="10,0,0,0" Orieation="Horizoal" > <TextBlock Margin="0,0,0,0" Text="{Binding Path=LogMessage}" TextWrapping="Wrap" VerticalAlignme="Top" HorizoalAlignme="Stretch"/> <ToggleButton x:Name="ExpandExceptiooggleButton" VerticalAlignme="Top" Margin="5,0,0,0" IsChecked="False" Coe="Show Details" Tag="Hide Details" Style="{StaticResource TextButtonStyle}" Foreground="{StaticResource BlueBrush}" Background="{StaticResource RedBrush}" Visibility="{Binding Path=HasException, Converter={StaticResource BoolToVisibilityConverter}}" /> </StackPanel> <Expander IsExpanded="{Binding Path=IsChecked, ElemeName=ExpandExceptiooggleButton}" Style="{StaticResource CoeExpanderStyle}" Margin="10,0,0,0" Grid.Column="4" Grid.Row="1"> <Border BorderBrush="{StaticResource DarkGreyBrush}" BorderThickness="1,0,0,0"> <TextBlock Text="{Binding Path=Exception}" Margin="5,0,0,0"/> </Border> </Expander> </Grid> </DataTemplate> 

You can see how much easier it is to define a title and an extensible body. If you have a need for nested data, add the Level property to your view model (you use MVVM, don't you?), And then create an IValueConverter that returns a Margin value (i.e. Thickness ) to fake the indent.

Hope this helps if you have any further questions, feel free to ask.

+3
source

Source: https://habr.com/ru/post/900644/


All Articles