Search code examples
c#.netwpfuser-interfacetreeview

WPF - How to align treeviewitems in a treeview so that they align with the columns?


I'm trying to get the child tree view items so that they align with the column header, regardless of the size of the previous columns. I want to avoid using a predefined margin space as it messes up with the resizing of certain elements.

Here's a picture to better illustrate what I want: example

Here's my tree view xaml:

<!-- TreeGrid "Control" -->
<Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch">

    <!-- Resources -->
    <Border.Resources>
        <Style TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TreeViewItem">
                        <StackPanel>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"
                        MinWidth="19" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition />
                                </Grid.RowDefinitions>
                                <ToggleButton IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Name="Expander">
                                    <ToggleButton.Style>
                                        <Style TargetType="ToggleButton">
                                            <Setter Property="UIElement.Focusable" Value="false" />
                                            <Setter Property="FrameworkElement.Width" Value="16" />
                                            <Setter Property="FrameworkElement.Height" Value="16" />
                                            <Setter Property="Control.Template">
                                                <Setter.Value>
                                                    <ControlTemplate TargetType="ToggleButton">
                                                        <Border Padding="5,5,5,5" Background="#00FFFFFF" Width="16" Height="16">
                                                            <Path Fill="#00FFFFFF" Stroke="#FF989898" Name="ExpandPath">
                                                                <Path.Data>
                                                                    <PathGeometry Figures="M0,0L0,6L6,0z" />
                                                                </Path.Data>
                                                                <Path.RenderTransform>
                                                                    <RotateTransform Angle="135"  CenterX="3" CenterY="3" />
                                                                </Path.RenderTransform>
                                                            </Path>
                                                        </Border>
                                                        <ControlTemplate.Triggers>
                                                            <Trigger Property="UIElement.IsMouseOver" Value="True">
                                                                <Setter TargetName="ExpandPath" Property="Shape.Stroke" Value="#FF1BBBFA" />
                                                                <Setter TargetName="ExpandPath" Property="Shape.Fill" Value="#00FFFFFF" />
                                                            </Trigger>
                                                            <Trigger Property="ToggleButton.IsChecked" Value="True">
                                                                <Setter TargetName="ExpandPath" Property="UIElement.RenderTransform">
                                                                    <Setter.Value>
                                                                        <RotateTransform Angle="180" CenterX="3" CenterY="3" />
                                                                    </Setter.Value>
                                                                </Setter>
                                                                <Setter TargetName="ExpandPath" Property="Shape.Fill" Value="#FF595959" />
                                                                <Setter TargetName="ExpandPath" Property="Shape.Stroke" Value="#FF262626" />
                                                            </Trigger>
                                                        </ControlTemplate.Triggers>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </ToggleButton.Style>
                                </ToggleButton>
                                <Border x:Name="Bd"
                                            HorizontalAlignment="Stretch"
                                            BorderThickness="{TemplateBinding Border.BorderThickness}"
                                            BorderBrush="{TemplateBinding Border.BorderBrush}"
                                            Padding="{TemplateBinding Control.Padding}"
                                            Background="{TemplateBinding Panel.Background}"
                                            SnapsToDevicePixels="True"
                                            Grid.Column="1">
                                    <ContentPresenter x:Name="PART_Header"
                                    Content="{TemplateBinding HeaderedContentControl.Header}"
                                    ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
                                    ContentStringFormat="{TemplateBinding HeaderedItemsControl.HeaderStringFormat}"
                                    ContentTemplateSelector="{TemplateBinding HeaderedItemsControl.HeaderTemplateSelector}"
                                    ContentSource="Header"
                                    HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
                                    SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                                </Border>
                                <ItemsPresenter x:Name="ItemsHost"
                    Grid.Column="1"
                    Grid.Row="1" />
                            </Grid>
                        </StackPanel>
                        <ControlTemplate.Triggers>
                            <Trigger Property="TreeViewItem.IsExpanded" Value="False">
                                <Setter TargetName="ItemsHost" Property="UIElement.Visibility" Value="Collapsed" />
                            </Trigger>
                            <Trigger Property="ItemsControl.HasItems"  Value="False">
                                <Setter TargetName="Expander" Property="UIElement.Visibility" Value="Hidden" />
                            </Trigger>
                            <Trigger Property="TreeViewItem.IsSelected" Value="True">
                                <Setter TargetName="Bd" Property="Panel.Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
                                <Setter Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="TreeViewItem.IsSelected" Value="True" />
                                    <Condition Property="Selector.IsSelectionActive" Value="False" />
                                </MultiTrigger.Conditions>
                                <Setter TargetName="Bd" Property="Panel.Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
                                <Setter Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
                            </MultiTrigger>
                            <Trigger Property="UIElement.IsEnabled" Value="False">
                                <Setter Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>


        <Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
            <!--<Setter Property="Margin" Value="3 0 3 0"/>-->
        </Style>
        <Style x:Key="TextBlockBoldStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle}">
            <Setter Property="FontWeight" Value="Bold"/>
        </Style>
    </Border.Resources>

    <!-- Content -->
    <Grid Grid.IsSharedSizeScope="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <!-- Column headers -->
        <TreeViewItem Grid.Row="0" BorderThickness="1">
            <TreeViewItem.Header>
                <Grid HorizontalAlignment="Stretch">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="1*"/>
                        <ColumnDefinition Width="0.4*"/>
                        <ColumnDefinition Width="0.4*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="Name" Style="{StaticResource TextBlockBoldStyle}"/>
                    <TextBlock Grid.Column="1" Text="Status" Style="{StaticResource TextBlockBoldStyle}"/>
                    <TextBlock Grid.Column="2" Text="Files" Style="{StaticResource TextBlockBoldStyle}"/>
                </Grid>
            </TreeViewItem.Header>
        </TreeViewItem>

        <!-- Data rows -->
        <TreeView x:Name="MainTreeView" Grid.Row="1" ItemsSource="{Binding SubItems}" BorderBrush="Gray" BorderThickness="0 1 0 0">
            <TreeView.ItemTemplate>
                
                <!-- Level 0 template leaves space for 2 child "Toggle" levels -->
                <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                    <Grid HorizontalAlignment="Stretch">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="1*"/>
                            <ColumnDefinition Width="0.4*"/>
                            <ColumnDefinition Width="0.4*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding Name}" Style="{StaticResource TextBlockStyle}"/>
                        <TextBlock Grid.Column="1" Text="{Binding StatusMsg}" Style="{StaticResource TextBlockStyle}"/>
                        <TextBlock Grid.Column="2" Text="{Binding FilesMsg}" Style="{StaticResource TextBlockStyle}"/>
                    </Grid>

                    <!-- Level 1 template leaves space for 1 child "Toggle" level -->
                    <HierarchicalDataTemplate.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                            <Grid HorizontalAlignment="Stretch" >
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="1*"/>
                                    <ColumnDefinition Width="0.4*"/>
                                    <ColumnDefinition Width="0.4*"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="{Binding Name}" Style="{StaticResource TextBlockStyle}"/>
                                <TextBlock Grid.Column="1" Text="{Binding StatusMsg}" Style="{StaticResource TextBlockStyle}"/>
                                <TextBlock Grid.Column="2" Text="{Binding FilesMsg}" Style="{StaticResource TextBlockStyle}"/>
                            </Grid>
                        </HierarchicalDataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Border>

I tried using a custom style that horizontally stretches the elements, but that still didn't work.


Solution

  • Ended up using this library: https://github.com/Anapher/MetroTreeListView, and changed some things to make it work in my project.

    Here's the final result: final result