TheJediCowboy
TheJediCowboy

Reputation: 9232

WPF ListBox TreeViewItem to Fill Width of TreeView

If I have a TreeView that uses ListBox controls as the TreeViewItems, how do I get the ListBoxItems to span the entire TreeView container.

 <Grid>
    <TreeView>
        <TreeViewItem Header="General 1">
            <ListBox BorderThickness="0">
                <ListBoxItem>Item A</ListBoxItem>
                <ListBoxItem>Item B</ListBoxItem>
                <ListBoxItem>Item C</ListBoxItem>
            </ListBox>
        </TreeViewItem>
        <TreeViewItem Header="General 2">
            <ListBox BorderThickness="0">
                <ListBoxItem>Item A</ListBoxItem>
                <ListBoxItem>Item B</ListBoxItem>
                <ListBoxItem>Item C</ListBoxItem>
            </ListBox>
        </TreeViewItem>
        <TreeViewItem Header="General 3">
            <ListBox BorderThickness="0">
                <ListBoxItem>Item A</ListBoxItem>
                <ListBoxItem>Item B</ListBoxItem>
                <ListBoxItem>Item C</ListBoxItem>
            </ListBox>
        </TreeViewItem>
        <TreeViewItem Header="General 4">
            <ListBox BorderThickness="0">
                <ListBoxItem>Item A</ListBoxItem>
                <ListBoxItem>Item B</ListBoxItem>
                <ListBoxItem>Item C</ListBoxItem>
            </ListBox>
        </TreeViewItem>
    </TreeView>
</Grid>

This produces a result such as the following image. Notice that the selected ListBoxItem does not span the width of the Treeview control.

enter image description here

Upvotes: 0

Views: 4404

Answers (2)

Sajeetharan
Sajeetharan

Reputation: 222722

Try applying this style to your TreeView,

  <Style TargetType="TreeViewItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TreeViewItem">
                    <StackPanel>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="ExpansionStates">
                                <VisualState x:Name="Expanded">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames
                                    Storyboard.TargetProperty="(UIElement.Visibility)"
                                    Storyboard.TargetName="ItemsHost">
                                            <DiscreteObjectKeyFrame KeyTime="0"
                                        Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Collapsed" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <ContentPresenter ContentSource="Header" />
                        <ItemsPresenter Name="ItemsHost" Visibility="Collapsed" />
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Upvotes: 1

123 456 789 0
123 456 789 0

Reputation: 10865

By default TreeViewItems behave the same way as what you have right now so ListBox has nothing to do with it. TreeViewItem just surrounds your ListBox with a ContentPresenter. In order for it to span the selected item highlight brush would require additional work.

You have several options, you can modify the Template for the TreeViewItem

<ControlTemplate TargetType="{x:Type TreeViewItem}">
<StackPanel>
    <Border Name="Bd"
      Background="{TemplateBinding Background}"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}"
      Padding="{TemplateBinding Padding}">
        <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="19" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
              <ToggleButton x:Name="Expander"
                      Style="{StaticResource ExpandCollapseToggleStyle}"
                      IsChecked="{Binding Path=IsExpanded,
                                  RelativeSource={RelativeSource TemplatedParent}}"
                      ClickMode="Press"/>
              <ContentPresenter x:Name="PART_Header"
                    Grid.Column="1"
                        ContentSource="Header"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
        </Grid>
  </Border>
  <ItemsPresenter x:Name="ItemsHost" Margin="19,0,0,0" />
</StackPanel>
<!-- Triggers -->
</ControlTemplate>

The problem with this is that it won't span to with the ToggleButton. It'll only span for the second column item. Where ExpandCollapseToggleStyle can be extracted by going to the actual control template.

The other solution where it'll span even with the toggle button included is using a Length Converter and a TreeViewItem extension method to get the Depth. Both of these are tightly coupled to the TreeViewItem visual tree, so if you start messing with the Templates then you may have troubles.

Define a style

Full TreeViewItem Style

<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
  <Setter Property="Focusable" Value="False"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="ToggleButton">
        <Grid
          Width="15"
          Height="13"
          Background="Transparent">
          <Path x:Name="ExpandPath"
            HorizontalAlignment="Left" 
            VerticalAlignment="Center" 
            Margin="1,1,1,1"
            Fill="{StaticResource GlyphBrush}"
            Data="M 4 0 L 8 4 L 4 8 Z"/>
        </Grid>
        <ControlTemplate.Triggers>
          <Trigger Property="IsChecked"
               Value="True">
            <Setter Property="Data"
                TargetName="ExpandPath"
                Value="M 0 4 L 8 4 L 4 8 Z"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
<Style x:Key="TreeViewItemFocusVisual">
  <Setter Property="Control.Template">
    <Setter.Value>
      <ControlTemplate>
        <Border>
          <Rectangle Margin="0,0,0,0"
                 StrokeThickness="5"
                 Stroke="Black"
                 StrokeDashArray="1 2"
                 Opacity="0"/>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>


<Style x:Key="{x:Type TreeViewItem}"
     TargetType="{x:Type TreeViewItem}">
  <Setter Property="Background"
      Value="Transparent"/>
  <Setter Property="HorizontalContentAlignment"
      Value="{Binding Path=HorizontalContentAlignment,
              RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
  <Setter Property="VerticalContentAlignment"
      Value="{Binding Path=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}">
        <ControlTemplate.Resources>
            <local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
        </ControlTemplate.Resources>
        <StackPanel>
        <Border Name="Bd"
              Background="{TemplateBinding Background}"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              Padding="{TemplateBinding Padding}">
            <Grid Margin="{Binding Converter={StaticResource lengthConverter},
                              RelativeSource={RelativeSource TemplatedParent}}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="19" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
          <ToggleButton x:Name="Expander"
                  Style="{StaticResource ExpandCollapseToggleStyle}"
                  IsChecked="{Binding Path=IsExpanded,
                              RelativeSource={RelativeSource TemplatedParent}}"
                  ClickMode="Press"/>

            <ContentPresenter x:Name="PART_Header"
            Grid.Column="1"
                      ContentSource="Header"
                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
            </Grid>
          </Border>
          <ItemsPresenter x:Name="ItemsHost" />
        </StackPanel>
        <ControlTemplate.Triggers>
          <Trigger Property="IsExpanded"
               Value="false">
            <Setter TargetName="ItemsHost"
                Property="Visibility"
                Value="Collapsed"/>
          </Trigger>
          <Trigger Property="HasItems"
               Value="false">
            <Setter TargetName="Expander"
                Property="Visibility"
                Value="Hidden"/>
          </Trigger>
          <MultiTrigger>
            <MultiTrigger.Conditions>
              <Condition Property="HasHeader"
                     Value="false"/>
              <Condition Property="Width"
                     Value="Auto"/>
            </MultiTrigger.Conditions>
            <Setter TargetName="PART_Header"
                Property="MinWidth"
                Value="75"/>
          </MultiTrigger>
          <MultiTrigger>
            <MultiTrigger.Conditions>
              <Condition Property="HasHeader"
                     Value="false"/>
              <Condition Property="Height"
                     Value="Auto"/>
            </MultiTrigger.Conditions>
            <Setter TargetName="PART_Header"
                Property="MinHeight"
                Value="19"/>
          </MultiTrigger>
          <Trigger Property="IsSelected"
               Value="true">
            <Setter TargetName="Bd"
                Property="Background"
                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 TargetName="Bd"
                Property="Background"
                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>

                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="19" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <ToggleButton x:Name="Expander"
                    Style="{StaticResource ExpandCollapseToggleStyle}"
                    IsChecked="{Binding Path=IsExpanded,
                    RelativeSource={RelativeSource TemplatedParent}}"
                    ClickMode="Press"/>

                <ContentPresenter x:Name="PART_Header"
                    Grid.Column="1"
                    ContentSource="Header"
                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
            </Grid>
      </Border>
      <ItemsPresenter x:Name="ItemsHost" />
    </StackPanel>
    <!-- Triggers -->
</ControlTemplate>

TreeViewDepth Extension

public static class TreeViewItemExtensions
{
    public static int GetDepth(this TreeViewItem item)
    {
        TreeViewItem parent;
        while ((parent = GetParent(item)) != null)
        {
            return GetDepth(parent) + 1;
        }
        return 0;
    }

    private static TreeViewItem GetParent(TreeViewItem item)
    {
        var parent = VisualTreeHelper.GetParent(item);
        while (!(parent is TreeViewItem || parent is TreeView))
        {
            parent = VisualTreeHelper.GetParent(parent);
        }
        return parent as TreeViewItem;
    }
}

LeftMarginMultiplierConverter

public class LeftMarginMultiplierConverter : IValueConverter
{
    public double Length { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var item = value as TreeViewItem;
        if (item == null)
            return new Thickness(0);

        return new Thickness(Length * item.GetDepth(), 0, 0, 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }
}

Control

<TreeView Margin="50">

    <TreeViewItem Header="General 1">
        <ListBox BorderThickness="0">
            <ListBoxItem>Item A</ListBoxItem>
            <ListBoxItem>Item B</ListBoxItem>
            <ListBoxItem>Item C</ListBoxItem>
        </ListBox>
    </TreeViewItem>
    <TreeViewItem Header="General 2">
        <ListBox BorderThickness="0">
            <ListBoxItem>Item A</ListBoxItem>
            <ListBoxItem>Item B</ListBoxItem>
            <ListBoxItem>Item C</ListBoxItem>
        </ListBox>
    </TreeViewItem>
    <TreeViewItem Header="General 3">
        <ListBox BorderThickness="0">
            <ListBoxItem>Item A</ListBoxItem>
            <ListBoxItem>Item B</ListBoxItem>
            <ListBoxItem>Item C</ListBoxItem>
        </ListBox>
    </TreeViewItem>
    <TreeViewItem Header="General 4">
        <ListBox BorderThickness="0">
            <ListBoxItem>Item A</ListBoxItem>
            <ListBoxItem>Item B</ListBoxItem>
            <ListBoxItem>Item C</ListBoxItem>
        </ListBox>
    </TreeViewItem>
</TreeView>

Upvotes: 2

Related Questions