5

I have a TreeView which simplified is defined as

<TreeView ItemsSource="{Binding TreeItems}">
    <TreeView.Resources>
        <DataTemplate DataType="{x:Type models:MyModel}">
            <Border Margin="{Binding Margin}" >
                 <Grid>
                     <TextBlock Text="{Binding Path=Name}" Margin="3,3,3,3" />
                 </Grid>
            </Border>
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

It looks like this

tree

As you can see due to the Margin, which is variable, there is space between the items. The problem is the dropdown arrow. It is not centered on the element. Well, it is centered on the element ignoring the margin. How do I adjust the arrow?

juergen d
  • 201,996
  • 37
  • 293
  • 362
  • position of arrow is set in a TreeViewItem template. try to make a custom control template for TreeViewItem [example here](https://stackoverflow.com/questions/19560466/how-to-make-wpf-treeview-style-as-winforms-treeview) – ASh Sep 18 '17 at 11:12
  • Coult you please include some sample data that can be used to reproduce your issue? And what do you mean by saying that the margin "is variable"? – mm8 Sep 18 '17 at 12:39
  • The Margin is a property of the object rendered as TreeViewItem. In the picture you can see that the Margin is not always the same for all items. Sometimes there are bigger gapes between the items. – juergen d Sep 18 '17 at 12:41
  • Adding example data is not that easy because it all comes dynamically from the DB. – juergen d Sep 18 '17 at 12:44
  • Create your own sample data that doesn't come from the DB: https://stackoverflow.com/help/mcve – mm8 Sep 18 '17 at 13:25

5 Answers5

7

Your XAML markup is both incomplete and incorrect: the DataTemplate should be a HierarchicalDataTemplate and it should be placed in a <TreeView.ItemTemplate> tag. This doesn't apply after your edit.

You can apply the margin to the complete TreeViewItem content including the dropdown arrow:

<TreeView ItemsSource="{Binding Items}">
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="Margin" Value="{Binding Margin}"/>
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
            <Border>
                <Grid>
                    <TextBlock Margin="3,3,3,3"/>
                </Grid>
            </Border>    
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>
dymanoid
  • 14,771
  • 4
  • 36
  • 64
  • Since it is not the same margin for all items I doubt this will work. Margin is a property of the object rendered as tree view item and can be different for each item. – juergen d Sep 18 '17 at 11:44
  • I did try it. The margin is gone for all items. – juergen d Sep 18 '17 at 11:46
  • I updated the question and added missing code. I did not add the triggers - those are irrelevant. – juergen d Sep 18 '17 at 11:55
  • You mean I can't adjust the arrows without a HierarchicalDataTemplate? Because they are there and visible without HierarchicalDataTemplate. – juergen d Sep 18 '17 at 11:59
  • The screenshot does match the XAML, believe me. I only stripped the irrelevant parts. There is another template though: `` – juergen d Sep 26 '17 at 11:12
  • I build the tree in my ViewModel code in C# and not in XAML. Iterating over a list and so on. – juergen d Sep 26 '17 at 11:35
  • 1
    Then you have to post an example of that. Have you ever heard something about a [mcve]? **Complete** and **Verifiable** is important. The little XAML snippet you posted doesn't produce the output that you show on the screenshot. And nobody wants to guess how is it done in your code. – dymanoid Sep 26 '17 at 11:38
1

Yes, I know the style below is too lengthy, but give it a try. Just put it in a separate resource dictionary and merge it.

<TreeView ItemsSource="{Binding TreeItems}" ItemContainerStyle="{StaticResource CenteredExpanderTreeViewItemStyle}">
    <TreeView.Resources>
        <DataTemplate DataType="{x:Type models:MyModel}">
            <Border Margin="{Binding Margin}" >
                 <Grid>
                     <TextBlock Text="{Binding Path=Name}" Margin="3,3,3,3" />
                 </Grid>
            </Border>
        </DataTemplate>
    </TreeView.Resources>
</TreeView>

CenteredExpanderTreeViewItemStyle.xaml

<Style x:Key="TreeViewItemFocusVisual">
    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate>
                <Rectangle/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
    <Setter Property="Focusable" Value="False"/>
    <Setter Property="Width" Value="16"/>
    <Setter Property="Height" Value="16"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Border Background="Transparent" Height="16" Padding="5,5,5,5" Width="16">
                    <Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
                        <Path.RenderTransform>
                            <RotateTransform Angle="135" CenterY="3" CenterX="3"/>
                        </Path.RenderTransform>
                    </Path>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="RenderTransform" TargetName="ExpandPath">
                            <Setter.Value>
                                <RotateTransform Angle="180" CenterY="3" CenterX="3"/>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
                        <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
                        <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsMouseOver" Value="True"/>
                            <Condition Property="IsChecked" Value="True"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
                        <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<Style x:Key="CenteredExpanderTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
    <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
    <Setter Property="Padding" Value="1,0,0,0"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TreeViewItem}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition MinWidth="19" Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <ToggleButton x:Name="Expander" VerticalAlignment="Center" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
                    <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                        <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                    <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsExpanded" Value="false">
                        <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
                    </Trigger>
                    <Trigger Property="HasItems" Value="false">
                        <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
                    </Trigger>
                    <Trigger Property="IsSelected" Value="true">
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsSelected" Value="true"/>
                            <Condition Property="IsSelectionActive" Value="false"/>
                        </MultiTrigger.Conditions>
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"/>
                    </MultiTrigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true">
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Trigger>
    </Style.Triggers>
</Style>
shadow32
  • 464
  • 3
  • 9
0

Use given style for TreeViewItem like give below

<Style TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
            <Setter Property="Margin" Value="3,3,3,3"></Setter>
        </Style>

Hope this help.

Vishwash Roy
  • 281
  • 2
  • 7
0

To add a margin to the ToggleButton in a TreeView, you have to add a custom ControlTemplate. There are several good tutorials on MSDN, for example here: https://msdn.microsoft.com/de-de/library/ms788727(v=vs.85).aspx

I stripped one example down to the necessary elements to match your requirements. Please note, that the ToggleButton is this example is not styled at all, so it appears a normal Button would do. But I am sure, you find several good examples on how to style this Button in a way that suites you the best.

The code:

<Window.Resources>
        <Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TreeViewItem}">
                        <Grid Margin="{Binding Margin}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition MinWidth="20" Width="Auto"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="*"></RowDefinition>
                            </Grid.RowDefinitions>
                            <ToggleButton Grid.Column="0" x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" />
                            <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Row="0" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                                <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1" Margin="-10,0,0,0"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="false">
                                <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="HasItems" Value="false">
                                <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="true">
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="true"/>
                                    <Condition Property="IsSelectionActive" Value="false"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                            </MultiTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true">
                    <Setter Property="ItemsPanel">
                        <Setter.Value>
                            <ItemsPanelTemplate>
                                <VirtualizingStackPanel/>
                            </ItemsPanelTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <TreeView ItemsSource="{Binding TreeItems}" ItemContainerStyle="{StaticResource TreeViewItemStyle}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type library:MyModel}" ItemsSource="{Binding Children}" >
                <TextBlock Text="{Binding Name}"></TextBlock>
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>
Eike B
  • 231
  • 1
  • 7
0

You should be able to do just this:

<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
    <Setter Property="Margin" Value="0,0,0,0" />
    <!--Or edit Padding if Margin doesn't work-->
</Style>

It will use default style of a TreeViewItem and edit only defined properties. If you use explicitly defined style for TreeViewItem, use BaseOn="{StaticResource [YourStyleKey]}" (without [ ]).

If you need some more advanced styling I'd suggest you to use Blend to get the ControlTemplate of a TreeViewItem, then just edit it to your needs, without changing too much (keep in mind that names of controls used as a template may have a crucial meaning, so be careful what you are editing).

If you don't know how to use Blend to get your control's template, here is a simple tutorial described in a documentation of Telerik controls (don't worry, it works the same for all controls). You just need to create copy of a TreeViewItem ControlTemplate, paste it to your application and you are good to go (editing).

By using Blend you can get templates of all building blocks of a TreeView control and make them look like you want them to. You just need to do some digging.