yu_ominae yu_ominae - 3 months ago 34
C# Question

Indenting top hierarchical items in a wpf treeview

I am trying to provide a separation (margin) between the top hierarchical items in a WPF

TreeView
. The problem is that I cannot figure out how to write the
Style
for it to apply only to the top items and not to every item.

The code for my
TreeView
looks like this:

<TreeView ItemContainerStyle="{StaticResource treeViewItemStyle}"
ItemsSource="{Binding Container.RootRules}"
KeyUp="treeView_KeyUp"
SelectedItemChanged="TreeView_SelectedItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type me:HybridForecastRulesViewModel}"
ItemsSource="{Binding Children}">
<Border Name="bd"
...
</Border>
</HierarchicalDataTemplate>

<HierarchicalDataTemplate DataType="{x:Type me:RootRulesViewModel}"
ItemsSource="{Binding Rules}">
<Grid>
...
</Grid>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>


I have a style for the treeViewItems like this:

<Style x:Key="treeViewItemStyle"
BasedOn="{StaticResource {x:Type TreeViewItem}}"
TargetType="{x:Type TreeViewItem}">

<Setter Property="Margin" Value="0,10,0,0" />

<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>

</Style>


But this style applies to items of both type (
RootRulesViewModel
and
HybridForecastRulesViewModel
), when I would like for it to only apply to items of type
RootRulesViewModel
. How can this be done?

And the icing on the cake would be for all
RootRulesViewModel
items to have a top
Margin
of 10, except or the first one.

Answer

TreeViewItem has no Level property to use.

So you have 2 options:

1) this. look at the TreeLevelConverter, it's interesting. They bind the control itself and use the converter to retrieve the Level. In your case, you can extend the converter so after it retrieves the Level, converts it in a Thickness instance to use as Margin.

2) you can create a Level property on your ViewModels (possibly in their base class in order to avoid code duplication). Then, every time that you add a child to a ViewModel node, you set the Level on that child. In the xaml, you bind the Margin property to the Level on the ViewModel, using a converter that returns different Thickness depending if the Level is 1 or not.

EDIT:

This is how you set a common Style for all the TreeViewItem:

<TreeView>
  <TreeView.ItemContainerStyle>
    <Style TargetType="{x:Type TreeViewItem}">
      <Setter 1 .../>
      <Setter 2 .../>
      <Setter Property="Margin"
              Value="{Binding Level, Converter={StaticResource LevelToMarginConverter}}"/>
    </Style>
  </TreeView.ItemContainerStyle>
  <TreeView.Resources>
    <!-- here your hierarchical DataTemplate... -->
    <HierarchicalDataTemplate ... />
  </TreeView.Resources>
</TreeView>