locked
TreeViewItem Templates and HorizontalAlignment RRS feed

  • Question

  • I have an application that is using a TreeView to present a list of items, grouped by date. The TreeView control is contained within a sizable grid column. When I resize the column of the grid, I want the width of the "child" items in the TreeView to stretch to fill the entire width of the grid column. The TreeView is configured to apply a different template to the selected "child" item using a Trigger, and that item does exactly what I want (stretches to fill the grid column width), however all of the unselected "child" items do not.

    With no child item selected, child items not stretching to fill column width (gap on right side):

    TreeView - No

    Selecting a child item causes that child item to stretch to fill column width (no gap):

    TreeView - First

    The current state is achieved using the following basic code:

    <ContentControl … >
        <ContentControl.Resources>
                <ResourceDictionary>
    
                <!-- Tree item look when it is selected -->
                <ContentControl x:Key="TreeItemDataSelected" HorizontalAlignment="Stretch" VerticalAlignment="Top" >
                    <Border Margin="10,3,3,3" BorderThickness="1" >
                    <!-- control definition removed for brevity -->
                    </Border>
                </ContentControl>
    
                <Style x:Key="OurTreeViewStyle" TargetType="TreeViewItem">
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="TreeViewItem">
                                        <ContentControl Content="{StaticResource ResourceKey=TreeItemDataSelected}" />
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
    
            </ResourceDictionary>
        </ContentControl.Resources>
    
        <TreeView Name="tvRunHistories" VerticalContentAlignment="Top" >
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type self:RunHistory}" ItemsSource="{Binding Members}" ItemContainerStyle="{StaticResource ResourceKey=OurTreeViewStyle}">                
                    <!-- display date at "root" nodes -->
    <StackPanel Orientation="Horizontal" Margin="0,5,0,5" > <!-- definition removed for brevity --> </StackPanel> </HierarchicalDataTemplate> <DataTemplate DataType="{x:Type self:RunHistoryItem}" > <Border Margin="-10,3,3,3" BorderThickness="1" HorizontalAlignment="Stretch" > <!-- control definition removed for brevity – same basic content as TreeItemDataSelected ContentControl above --> </Border> </DataTemplate> </TreeView.Resources> </TreeView> </ContentControl>

    The problem seems to be that the "DataTemplate" content does not honor the HorizontalAlignment setting, but for selected items, when the Template gets changed to TreeItemDataSelected, its HorizontalAlignment setting is honored.

    I have tried setting up an additional trigger to apply a different template when IsSelected is false, but that did not have the desired effect. I also tried enclosing the DataTemplate <Border> control inside a <ContentControl> and setting HorizontalAlignment of the <ContentControl> to Stretch, with no effect.

    Any ideas on how to get unselected child items to stretch like the selected item does?


    • Edited by ACordner Wednesday, March 2, 2016 12:18 AM Updated things tried already
    Wednesday, March 2, 2016 12:10 AM

Answers

  • Avoid replacing the template for the treeviewitem.

    Don't do this.

    <Setter.Value>
        <ControlTemplate TargetType="TreeViewItem">
               <ContentControl Content="{StaticResource ResourceKey=TreeItemDataSelected}" />
               </ControlTemplate>
        </Setter.Value>
    

    Instead, you should be templating what goes in the treeviewitem.

    eg

          <HierarchicalDataTemplate DataType    = "{x:Type src:League}"
                                    ItemsSource = "{Binding Path=Divisions}">
            <TextBlock Text="{Binding Path=Name}"/>
          </HierarchicalDataTemplate>


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Wednesday, March 2, 2016 3:40 PM

All replies

  • My first candidate would be the approach explained here:

    http://leecampbell.blogspot.co.uk/2009/01/horizontal-stretch-on-treeviewitems.html

    Which entirely replaces the template for a treeviewitem.

    <Style TargetType="TreeViewItem"
           BasedOn="{StaticResource {x:Type TreeViewItem}}">
      <Setter Property="HorizontalContentAlignment"
              Value="Center" />
      <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>
                <!--
                                 Note that the following do not work, but I believe the top 2 should?!
                                 <ToggleButton IsChecked="{TemplateBinding IsExpanded}" ClickMode="Press" Name="Expander">
                                 <ToggleButton IsChecked="{TemplateBinding Property=IsExpanded}" ClickMode="Press" Name="Expander">
                                 <ToggleButton IsChecked="{TemplateBinding Path=IsExpanded}" ClickMode="Press" Name="Expander">
                            -->
                <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>


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Wednesday, March 2, 2016 9:08 AM
  • Andy,

    Thanks for the response. I found that site and tried to use that solution right before I wrote up this request. All that did for me was make the root items (dates) stretch across the width of the grid column. It did not affect the "child" items at all.

    Based on how the selected "child" item behaves when I replace the template for the selected item using the Trigger, it appears to be possible, I'm just not sure what is missing. Somehow I need to be able to apply a template for all non-selected items in the same way as the TreeItemDataSelected template is applied for the selected item.

    Alan

    Wednesday, March 2, 2016 3:32 PM
  • Avoid replacing the template for the treeviewitem.

    Don't do this.

    <Setter.Value>
        <ControlTemplate TargetType="TreeViewItem">
               <ContentControl Content="{StaticResource ResourceKey=TreeItemDataSelected}" />
               </ControlTemplate>
        </Setter.Value>
    

    Instead, you should be templating what goes in the treeviewitem.

    eg

          <HierarchicalDataTemplate DataType    = "{x:Type src:League}"
                                    ItemsSource = "{Binding Path=Divisions}">
            <TextBlock Text="{Binding Path=Name}"/>
          </HierarchicalDataTemplate>


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Wednesday, March 2, 2016 3:40 PM
  • Andy, 

    I will leave this marked as the correct answer, although I had to abandon the implementation for now due to time limits. I hope to be able to get this working in the near future.

    Thanks!

    Wednesday, March 16, 2016 2:29 PM