How to highlight entire row in WPF TreeView
Asked Answered
C

4

6

I want to adapt the default WPF TreeView/TreeViewItem template, so that the entire row can be highlighted, as shown in the image:

enter image description here

However, I've tried several templates from googling, but none of them can really make the effects, some wrong answers were even marked as correct...

There was an answer in codeflow which seems to work, but it add extra C# code, or didn't need extra code but not working perfectly.

I don't want to add extra C# code, but only change the default template. Anyone have any good ideas?

Thanks a lot!

----EDIT----

@Nick, after using your template, it is shown like this,

first, it didn't highlight the "entire" row, by "entire" I mean the widest width of the tree.

second, it highlighted extra areas including the children.

enter image description here

Ciapha answered 10/12, 2014 at 10:34 Comment(5)
1) Override the default template 2) Using Snoop find the exact panel which you need to highlight 3) Add background color for that panel in Trigger, 'OnSelected' property.Quite
@Kumar This question is actually more difficult than we all initially think, the TreeViewItem uses the recursive structure, which makes it very different from other controlsCiapha
Yes, you are right; Let me check further and get back to you.Quite
It's highlighting the children because the Trigger Setters are targeting the <Grid> which contains the item and its children. You're gonna want to add an empty rectangle using Grid.Column="0" Grid.ColumnSpan="3" and then use those Setters to change its background instead. This won't fix the problem with whitespace on the left of the expander, but it will fix children being highlighted.Showdown
This is the only solution I have found, and it works perfectly: stackoverflow.com/a/37784413Corybantic
A
3

I recently had to do something similar and though the question is quite old, figured others who stumble upon this page like I did might be able to get something from my solution.

I also edited the ControlTemplate, much like how Tim described in the comments. I grouped the "Expander" toggle button and the "Bd" border controls to a single grid that stretches across the full width of the TreeView. Then under the IsSelected trigger, I set the highlight to the newly created grid instead of the border.

Here is the TreeView's ControlTemplate with my edits:

<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>
    <!-- created ItemRowGrid to contain both togglebutton and border -->
    <Grid x:Name="ItemRowGrid" Grid.ColumnSpan="3" Margin="1.5,0,0,0">
        <ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}" HorizontalAlignment="Left" d:LayoutOverrides="Width, LeftMargin, RightMargin"/>
        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true" Margin="17.5,0,-17.5,0">
            <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
        </Border>
    </Grid>
    <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="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
        <!-- setting highlight target to created ItemRowGrid instead of border -->
        <Setter Property="Background" TargetName="ItemRowGrid" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
    </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>

It is not exactly the way the OP asked for because the highlight for the "children" rows start with an indent. But it does properly highlight each row without including sub-items.

Abyss answered 13/10, 2017 at 9:49 Comment(0)
H
1

You need to modify the template of an ItemContainerStyle. If you right click on your TreeView in Blend and select "Edit Additional Templates" -> "Edit Generated Item Container" -> "Edit a Copy". It will create a copy of default template in your xaml. Then you need to find the following bit of code there:

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

Look at the trigger that is activated by IsSelected property. You can see that it modifies the Background property of an element named "Bd". That's a border around text. If you want to expand that selection to the whole row you need to:

  1. Give name to a grid on the second line of the code example above.

  2. Modify the Setter of that IsSelected property trigger to target the grid instead of a border control. This will make sure the whole row is highlighted when you click on it.

  3. Modify the Setter of a MultiTrigger below to also target the grid instead of a border control. This will make sure the whole row is highlighted when it is selected, but the window is not in focus.

Here is the modified version that will apply selection to the whole row:

      <ControlTemplate TargetType="{x:Type TreeViewItem}">
        <Grid x:Name="itemGrid">
          <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" 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="itemGrid" 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="itemGrid" 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>
Homoiousian answered 10/12, 2014 at 11:31 Comment(2)
I tried, it didn't work, 1. it didn't apply to entire line 2. it highlighted extra areas, please refer to my post edit.Ciapha
BTW, this question is actually more difficult than we all initially think, the TreeViewItem uses the recursive structure, which makes it very different from other controls.Ciapha
I
1

Have you found the solution? If not then simply change ColumnDefination of column 1 and 2 like below

<ControlTemplate TargetType="{x:Type TreeViewItem}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition MinWidth="19" Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>

Full Code:

<ControlTemplate TargetType="{x:Type TreeViewItem}">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition MinWidth="19" Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <ToggleButton x:Name="Expander" 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>

This works for me

Infeudation answered 8/1, 2020 at 6:9 Comment(1)
This is an alternative solution with indented selection, but not what the OP desired to select the entire row.Fenton
S
0

not the best solution, but I thought I mention it anyways. simply overlaying a white grid does the job. overwrite the ItemsPresenter with the following:

<Grid Grid.Row="1"                                  
      Grid.Column="0"                                  
      Grid.ColumnSpan="3"
      >
        <Grid.ColumnDefinitions>
        <ColumnDefinition MinWidth="19" Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.Style>
        <Style TargetType="Grid">
            <Setter  Property="Background" Value="White"/>
        </Style>
    </Grid.Style>
    <ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.ColumnSpan="2"/>
</Grid>
Scofield answered 7/9, 2017 at 11:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.