Add a border around all children of a TreeViewItem - wpf

I have a TreeView and I am trying to implement a style that will allow me to place a border around all the children of a particular node using the HierarchicalDataTemplate. An example of what I want is shown below:
The following code is what I have so far.
<HierarchicalDataTemplate DataType="{x:Type model:Node}" ItemsSource="{Binding Children, Mode=OneWay}">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
//what goes in here???
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
What do I need to add to implement my border the way I want?

To render a Border around the collection of children for a TreeViewItem we need to modify the Style for ItemContainerStyle of the TreeView
TreeViewItem Style by default uses a <ItemsPresenter x:Name="ItemsHost" /> to render it's children's content.
Children's Content in the default ItemContainerStyle is given by
<ItemsPresenter x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2" />
Now to test this I had a Collection with a bool named Type and just tried to render a Border when this bool was True
So I updated the ItemsPresenter to
<Border Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
BorderThickness="1">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="BorderBrush"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TreeViewItem}},
Path=DataContext.Type}"
Value="True">
<Setter Property="BorderBrush"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<ItemsPresenter x:Name="ItemsHost" />
</Border>
Which then rendered the following
You'll of course have to update the above Bindings to be based on your own cases of when you want the Border rendered.
In my case my Type variable was set to True for the Item with "1.1" as it's Header Content.

Looks like WPF team thought that nobody will need this functionality so they haven't included any border around ItemsPresenter in TreeViewItem template, so you are going to have to change TreeViewItem template and add border around ItemsPresenter.
You can view default TreeViewItem style/template definition by downloading WPF themes XAML dictionaries. Links are provided here.
Here is a complete XAML of a working solution:
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:WpfApplication">
<Window.DataContext>
<x:ArrayExtension Type="{x:Type model:Node}">
<model:Node Name="Root">
<model:Node.Children>
<model:Node Name="Child 1" HasChildrenBorder="True">
<model:Node.Children>
<model:Node Name="Child 1.1"/>
<model:Node Name="Child 1.2"/>
<model:Node Name="Child 1.3"/>
</model:Node.Children>
</model:Node>
<model:Node Name="Child 2"/>
</model:Node.Children>
</model:Node>
</x:ArrayExtension>
</Window.DataContext>
<TreeView ItemsSource="{Binding}">
<TreeView.Resources>
<!--This part is extracted from Areo.NormalColor.xaml WPF Theme which you can download from locations explained here: https://stackoverflow.com/questions/4158678/where-can-i-download-microsofts-standard-wpf-themes-from/4158681#4158681-->
<PathGeometry x:Key="TreeArrow">
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsFilled="True"
StartPoint="0 0"
IsClosed="True">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="0 6"/>
<LineSegment Point="6 0"/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
<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 Width="16"
Height="16"
Background="Transparent"
Padding="5,5,5,5">
<Path x:Name="ExpandPath"
Fill="Transparent"
Stroke="#FF989898"
Data="{StaticResource TreeArrow}">
<Path.RenderTransform>
<RotateTransform
Angle="135"
CenterX="3"
CenterY="3"/>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF1BBBFA"/>
<Setter TargetName="ExpandPath" Property="Fill" Value="Transparent"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="ExpandPath" Property="RenderTransform">
<Setter.Value>
<RotateTransform
Angle="180"
CenterX="3"
CenterY="3"/>
</Setter.Value>
</Setter>
<Setter TargetName="ExpandPath" Property="Fill" Value="#FF595959"/>
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF262626"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Border Name="Bd"
Grid.Column="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<Border Name="ItemsHostBd"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2">
<ItemsPresenter x:Name="ItemsHost"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="ItemsHostBd" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="False">
<Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
</Trigger>
<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>
<!-- This part is customized to work with HasChildrenBorder property from data-bound object. -->
<DataTrigger Binding="{Binding HasChildrenBorder}" Value="True">
<Setter TargetName="ItemsHostBd" Property="BorderBrush" Value="Red"/>
<Setter TargetName="ItemsHostBd" Property="BorderThickness" Value="1"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<HierarchicalDataTemplate DataType="{x:Type model:Node}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Window>
Here is how Node class is defined:
using System.Collections.ObjectModel;
namespace WpfApplication
{
public class Node
{
public string Name { get; set; }
public ObservableCollection<Node> Children { get; set; }
public bool HasChildrenBorder { get; set; }
public Node()
{
this.Children = new ObservableCollection<Node>();
}
}
}

Related

WPF TreeView: How to make controls align relative to every TreeViewItem, yet still get the indentation effect for the header text?

Here's a visual idea of what I'm trying to accomplish:
I want all the buttons on the left and right sides to align vertically, no matter where the TreeViewItem is located in the tree. I'm having trouble achieving this effect while also getting the header to indent in a typical nested way.
My closest attempt involved modifying the TreeViewItem template; putting the buttons in a DockPanel (docking left or right) that spans all columns in the main grid, putting the expander & header in the middle column, and having the ItemsPresenter (ItemsHost) span all columns on the following row. This makes everything align, including the header stuff.
Here's a simplified version of what I currently have for my TreeViewItem style:
<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>
<HierarchicalDataTemplate x:Key="HeaderTemplate" DataType="{x:Type models:Entity}" ItemsSource="{Binding Path=Entities}">
<Label VerticalAlignment="Center">test</Label>
</HierarchicalDataTemplate>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate" Value="{StaticResource HeaderTemplate}"></Setter>
<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="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="HorizontalAlignment" Value="Left"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid HorizontalAlignment="Left" Width="300">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="0" Margin="1,0,0,0">
<Button
DockPanel.Dock="Left"/>
<Button
DockPanel.Dock="Left"/>
<Button
DockPanel.Dock="Left"
Margin="0"/>
<Button
DockPanel.Dock="Right"
HorizontalAlignment="Right"/>
<Button
DockPanel.Dock="Right"
HorizontalAlignment="Right"/>
<Button
DockPanel.Dock="Right"
HorizontalAlignment="Right"/>
</DockPanel>
<StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="0" Margin="25,0,0,0">
<ToggleButton x:Name="Expander" ClickMode="Press" Margin="15,0,0,0" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</StackPanel>
<ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Width="305"/>
</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>
How can I get the header to nest like in the picture while maintaining the docked buttons' alignment?
As you've probably figured out by now, this isn't exactly trivial. The problem is that unlike ListBox and DataGrid et al, TreeView doesn't have a row-like structure. Instead, it uses a hierarchical structure that looks somewhat like this:
There's a grid that wraps the entire control, but for any given "row" the TreeViewItem doesn't extend all the way over to the left where you want to place your buttons.
So to implement this, you're going to have to re-template TreeViewItem. The default template uses a grid to place its content, with the line content on row 0 and the children (if any) on row 1. Importantly, the children are also placed in column 1, which is how TreeView does its indenting. So the first step is to put an extra 3 columns on the left, plus another 3 on the right, to accommodate the six buttons you want to add to each row. Then you'll want to change the Grid.Column and Grid.ColumnSpan for the ItemsPresenter at each level so that it takes up the width of the entire control.
The problem now, of course, is that you've lost your indenting, so you'll have to add another column to the grid to add that back in. To do indenting properly for any given level, you need to know the indent for its parent, which was previously inherent in the layout itself but has now been stripped out. There are several solutions to this, but the easiest IMO is to use an attached property, which I'll call TreeViewItemHelper.Indent. For each ItemsPresenter in your TreeView you'll want to calculate the indentation to use for all the children below the current level:
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="10" Grid.Column="0" Grid.Row="1"
local:TreeViewItemHelper.Indent="{Binding Path=(local:TreeViewItemHelper.Indent), Mode=OneWay, RelativeSource={RelativeSource AncestorType=ItemsPresenter}, Converter={StaticResource IndentConverter}}" />
Note that I'm not actually using the value for anything here, just calculating what it should be by binding each TreeViewItemHelper.Indent to one in the previous level and running it through a converter which simply adds a fixed amount (i.e. the width of the ToggleButtons used to expand the tree nodes):
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new GridLength(((GridLength)value).Value + IndentSize);
}
Finally, you need to actually apply the indentation somewhere. We've already created a column in our grid for it, so we can just add a dummy control to that column and bind its width to the indentation level of whatever ItemsPresenter it's sitting inside:
<Rectangle Grid.Column="3" Width="{Binding Path=(local:TreeViewItemHelper.Indent).Value, Mode=OneWay, RelativeSource={RelativeSource AncestorType=ItemsPresenter}}" Fill="Transparent"/>
Add all that together, shake well, and here's what the XAML looks like to re-template TreeViewItem and assign it with a style:
<local:IndentConverter x:Key="IndentConverter" />
<ControlTemplate x:Key="TreeViewItemControlTemplate1" TargetType="{x:Type TreeViewItem}">
<Grid x:Name="tvGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<!--<ColumnDefinition Width="0"/>-->
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Left buttons -->
<Button Grid.Column="0" Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" />
<Button Grid.Column="1" Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" />
<Button Grid.Column="2" Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" />
<Rectangle Grid.Column="3" Width="{Binding Path=(local:TreeViewItemHelper.Indent).Value, Mode=OneWay, RelativeSource={RelativeSource AncestorType=ItemsPresenter}}" Fill="Transparent"/>
<ToggleButton x:Name="Expander" Grid.Column="4" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}">
<ToggleButton.Style>
<Style 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" Width="16">
<Path x:Name="ExpandPath" Data="M0,0 L0,6 L6,0 z" Fill="White" Stroke="#FF818181">
<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="#FF595959"/>
<Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" TargetName="ExpandPath" Value="#FF27C7F7"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="#FFCCEEFB"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1CC4F7"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="#FF82DFFB"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="5" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
<ContentPresenter x:Name="PART_Header" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<!-- Right buttons -->
<Button Grid.Column="7" Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" />
<Button Grid.Column="8" Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" />
<Button Grid.Column="9" Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" />
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="10" Grid.Column="0" Grid.Row="1"
local:TreeViewItemHelper.Indent="{Binding Path=(local:TreeViewItemHelper.Indent), Mode=OneWay, RelativeSource={RelativeSource AncestorType=ItemsPresenter}, Converter={StaticResource IndentConverter}}" />
</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>
<Style TargetType="TreeViewItem">
<Setter Property="Template" Value="{DynamicResource TreeViewItemControlTemplate1}" />
<Setter Property="IsExpanded" Value="True" />
</Style>
Here is the class for the attached property it uses:
public static class TreeViewItemHelper
{
public static GridLength GetIndent(DependencyObject obj)
{
return (GridLength)obj.GetValue(IndentProperty);
}
public static void SetIndent(DependencyObject obj, GridLength value)
{
obj.SetValue(IndentProperty, value);
}
// Using a DependencyProperty as the backing store for Indent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IndentProperty =
DependencyProperty.RegisterAttached("Indent", typeof(GridLength), typeof(TreeViewItemHelper), new PropertyMetadata(new GridLength(0)));
}
And finally the indentation converter:
public class IndentConverter : IValueConverter
{
private const int IndentSize = 16; // hard-coded into the XAML template
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new GridLength(((GridLength)value).Value + IndentSize);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
And here's the result:

How do I change a Treeview item toggle appeareance

I am making a simple program using WPF. I am pretty new to XAML and WPF.
I want to change the + button that collapses and expands the TreeView.
I've tried setting different styles for the Tree View Item in the Tree View, but without success. Below is what I've manage to write. I have Stackpanels with a TreeView. I use my first TreeView Item as my main tree.
<StackPanel>
<TreeView x:Name="MainTree" FontSize="13" HorizontalAlignment="Left"
BorderThickness="0" VerticalAlignment="Top" Height="400" Width="200">
<TreeViewItem x:Name="TreeParent" FontSize="15" Header="Categories" >
<TreeViewItem Header="{Binding CategoryName}" />
</TreeViewItem>
</TreeView>
</StackPanel>
</StackPanel>
I want to change the button to look like an arrow, instead of the + sign.
The default Windows 'Aero' style is an arrow. The control template XAML for that looks like this:
<Window.Resources>
<ResourceDictionary>
<PathGeometry x:Key="TreeArrow">
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsFilled="True"
StartPoint="0 0"
IsClosed="True">
<PathFigure.Segments>
<PathSegmentCollection>
<LineSegment Point="0 6"/>
<LineSegment Point="6 0"/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
<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 Width="16"
Height="16"
Background="Transparent"
Padding="5,5,5,5">
<Path x:Name="ExpandPath"
Fill="Transparent"
Stroke="#FF989898"
Data="{StaticResource TreeArrow}">
<Path.RenderTransform>
<RotateTransform Angle="135"
CenterX="3"
CenterY="3"/>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="ExpandPath"
Property="Stroke"
Value="#FF1BBBFA"/>
<Setter TargetName="ExpandPath"
Property="Fill"
Value="Transparent"/>
</Trigger>
<Trigger Property="IsChecked"
Value="True">
<Setter TargetName="ExpandPath"
Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="180"
CenterX="3"
CenterY="3"/>
</Setter.Value>
</Setter>
<Setter TargetName="ExpandPath"
Property="Fill"
Value="#FF595959"/>
<Setter TargetName="ExpandPath"
Property="Stroke"
Value="#FF262626"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle />
</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}">
<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"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Border Name="Bd"
Grid.Column="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"/>
</Grid>
<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>
<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.Triggers>
<Trigger Property="VirtualizingStackPanel.IsVirtualizing"
Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
Take that big chunk of XAML above; everything in between <Window.Resources> and </Window.Resources> and put it right at the top of your Window.xaml file (or wherever you are using your TreeView). That will override the default style of the TreeView and replace it with this one that has the arrow instead of the plus sign.
It produces results that look like this:

WPF TreeView with right alignment values

I have a problem with my wpf treeview. It's code goes like this:
<TreeView ItemsSource="{Binding Path=Items}" Grid.RowSpan="2" Grid.ColumnSpan="2">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type m:MyTreeItem}" ItemsSource="{Binding Items}">
<DockPanel LastChildFill="True">
<TextBlock Text="{Binding Path=Value}" DockPanel.Dock="Right"/>
<TextBlock Text="{Binding Path=Display}" DockPanel.Dock="Left"/>
</DockPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Where MyTreeItem is a simple class with 2 String properties (Display and Value) and a List of MyTreeItem called Items.
I need the tree view to display all 'Values' aligned to the right, while maintaining the tree tabulation of the items 'Display', based on it's deepth.
I have tryed to set the dockPanel to a fixed width, but that didn't work.
I want to do this without any c# code, just xaml.
Thanks in advance.
If I understand your question correctly you want to Stretch the TreeViewItems and align Value to the right. To get the TreeViewItems to Stretch Horizontaly you'll need to edit the Template for TreeViewItem. The problem is explained here: http://leecampbell.blogspot.com/2009/01/horizontal-stretch-on-treeviewitems.html
Here is an edited version of the TreeViewItem which does this. Note that you'll have to add a reference to PresentationFramework.Aero for this to work
Sample
<TreeView ItemsSource="{Binding Path=Items}"
Grid.RowSpan="2"
Grid.ColumnSpan="2"
ItemContainerStyle="{StaticResource StretchTreeViewItemStyle}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type m:MyTreeItem}" ItemsSource="{Binding Items}">
<DockPanel LastChildFill="True">
<TextBlock Text="{Binding Path=Value}" DockPanel.Dock="Right"/>
<TextBlock Text="{Binding Path=Display}" DockPanel.Dock="Left"/>
</DockPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
StretchTreeViewItemStyle
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
<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="Transparent" Stroke="#FF989898">
<Path.RenderTransform>
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="Transparent"/>
</Trigger>
<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="#FF595959"/>
<Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="StretchTreeViewItemStyle"
TargetType="{x:Type TreeViewItem}"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<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="*"/>
</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.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>
</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>
class StretchedTreeView : TreeView {
protected override void OnItemContainerStyleChanged(Style oldItemContainerStyle, Style newItemContainerStyle) {
base.OnItemContainerStyleChanged(oldItemContainerStyle, newItemContainerStyle);
newItemContainerStyle.Setters
.Add(new EventSetter(LoadedEvent, new RoutedEventHandler(TreeViewItem_Loaded)));
}
void TreeViewItem_Loaded(object sender, RoutedEventArgs e) {
TreeViewItem item = sender as TreeViewItem;
if (item == null) {
return;
}
ContentPresenter cp = FindVisualChild<ContentPresenter>(item);
cp.HorizontalAlignment = HorizontalAlignment.Stretch;
Border border = FindVisualChild<Border>(item, "Bd");
border.SetValue(Grid.ColumnSpanProperty, 2);
}
static T FindVisualChild<T>(DependencyObject obj, string name = null) where T : DependencyObject {
for (int i = 0 ; i < VisualTreeHelper.GetChildrenCount(obj) ; i++) {
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T
&& (name == null || !(child is FrameworkElement) || name == ((FrameworkElement) child).Name)) {
return (T) child;
}
else {
T childOfChild = FindVisualChild<T>(child, name);
if (childOfChild != null) {
return childOfChild;
}
}
}
return null;
}
}

WPF: Refine the look of a grouped ComboBox vs. a grouped DataGrid -sample attached -

just watch this screenshot I made so you see the difference:
I have these requirments to be changed in the ComboBox`s PopUp so it looks like the grouped WPF DataGrid, its only about the Popup-area, do not judge the editable area of the ComboBox or that there is no Header... Important are these things, because I could not change them:
ComboBox:
(Green Line) The alternating
Background of the Item must start at
the beginning
(Red Line) The TextBlocks within the
Border must be aligned Center OR
Right1.
(Blue) The weakly visible horizontal
Border must always stretch to the
right side or take all space2.
to 1.) I have no idea why there is a margin
to 2.) HorizontalAlignment of the TextBlock does not work
to 3.) I can make the stackpanel in the ItemTemplate of the Combobox a read background then you can see very well the red color has a margin somehow on the right and left side. Have no idea how to remove that.
Anyone can help, please?
If you want to see the textbox live just download it here:
http://www.sendspace.com/file/6lmbrh
Its a 30 kb VS2010 .NET 4.0 project.
Here is the XAML for the ComboBox:
<Window x:Class="TestComboGrouped.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="200">
<Window.Resources>
<Style x:Key="ComboBoxItemStyle" TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="Red"/>
<Style.Triggers>
<Trigger Property="ComboBox.AlternationIndex" Value="0">
<Setter Property="Background" Value="White"></Setter>
</Trigger>
<Trigger Property="ComboBox.AlternationIndex" Value="1">
<Setter Property="Background" >
<Setter.Value>
<LinearGradientBrush RenderOptions.EdgeMode="Aliased" StartPoint="0.5,0.0" EndPoint="0.5,1.0">
<GradientStop Color="#FFFEFEFF" Offset="0"/>
<GradientStop Color="#FFE4F0FC" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ComboBoxBorderStyle" TargetType="Border">
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="BorderBrush" Value="#FFCEDFF6" />
<Setter Property="BorderThickness" Value="1 0 0 1" />
</Style>
<Style x:Key="ComboBoxStyle" BasedOn="{StaticResource {x:Type ComboBox}}" TargetType="{x:Type ComboBox}">
<Setter Property="ItemContainerStyle" Value="{StaticResource ComboBoxItemStyle}"/>
</Style>
<!-- Grouped CollectionView -->
<CollectionViewSource Source="{Binding WeeklyDateList,IsAsync=False}" x:Key="WeeklyView">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="MonthName"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<StackPanel>
<ComboBox
ItemsSource="{Binding Source={StaticResource ResourceKey=WeeklyView}}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
Style="{StaticResource ComboBoxStyle}"
AlternationCount="2"
MaxDropDownHeight="300"
Width="Auto"
x:Name="comboBox"
>
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock
Padding="5,0,0,0"
Background="White"
Foreground="DarkBlue"
FontSize="14"
FontWeight="DemiBold"
Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border Style="{StaticResource ComboBoxBorderStyle}">
<TextBlock Width="100" Foreground="Purple" Text="{Binding WeeklyLessonDate, StringFormat='yyyy-MM-dd'}"/>
</Border>
<Border Style="{StaticResource ComboBoxBorderStyle}">
<TextBlock Padding="5,0,5,0" Width="40" Text="{Binding WeekNumber}"/>
</Border>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Window>1.2.
UPDATE:
#Meleak thats the updated image it looks very nice thanks to you:
I was just about to put my old 2-"ColumnHeader" in that ComboBox`s Popup top area but I could not find anymore my sample... seems due to changing/trying a lot I have overritten that code :/ I know I did it in the controltemplate above the scrollviewer with a stackpanel or a grid with 2 rowdefinitions. But your code looks now totally different to my default combobox controltemplate I have no idea how to merge both code snippets.
I think that was the code where I put the 2 "column headers", just search for inside the POPUP
<Style x:Key="ComboBoxStyle1" TargetType="{x:Type ComboBox}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ComboBoxFocusVisual}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Padding" Value="4,3"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}">
<Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
// Here should be the column headers, but how merge your code with the ItemsPresenter?
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Microsoft_Windows_Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxReadonlyToggleButton}"/>
<ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="DropDownBorder" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
<Setter Property="Background" Value="#FFF4F4F4"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEditable" Value="true">
<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="Template" Value="{StaticResource ComboBoxEditableTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
I think it looks pretty close. You can download my modified version from here
Added a template for ComboBoxItem
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<Style x:Key="ComboBoxItemStyle" TargetType="ComboBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Grid HorizontalAlignment="Stretch"
Margin="-5,0,0,0"
Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="45"/>
</Grid.ColumnDefinitions>
<Border Name="border1"
BorderThickness="0,0,1,1"
BorderBrush="#FFCEDFF6"
Grid.Column="0">
<TextBlock Foreground="Purple"
HorizontalAlignment="Right"
Margin="0,0,2,0"
Text="{Binding WeeklyLessonDate, StringFormat='yyyy-MM-dd'}"/>
</Border>
<Border Name="border2"
BorderThickness="0,0,1,1"
BorderBrush="#FFCEDFF6"
Grid.Column="1">
<TextBlock HorizontalAlignment="Center"
Text="{Binding WeekNumber}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="border1" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
<Setter TargetName="border2" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Red"/>
<Style.Triggers>
<Trigger Property="ComboBox.AlternationIndex" Value="0">
<Setter Property="Background" Value="White"></Setter>
</Trigger>
<Trigger Property="ComboBox.AlternationIndex" Value="1">
<Setter Property="Background" >
<Setter.Value>
<LinearGradientBrush RenderOptions.EdgeMode="Aliased" StartPoint="0.5,0.0" EndPoint="0.5,1.0">
<GradientStop Color="#FFFEFEFF" Offset="0"/>
<GradientStop Color="#FFE4F0FC" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>

Highlight whole TreeViewItem line in WPF

If I set TreeViewItem Background it highlights the header only. How can I highlight the whole line?
I have found a post almost solving a problem http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/b04f73e2-0b10-4d97-a6da-64df2e30c21d/
But there are some problems:
1. It does not highlight the whole line
2. The tree has XP style on Vista. I whould like it to looks the same way on Vista as it was, but if user changed the theme to XP - it should be XP way.
3. So many XAML...
Any ideas, what I should look for?
Here we go, third times a charm. If you want something that look like this.
This one takes a bit more work. I'm sure there are many ways of doing this, but this method uses 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. Again, here is the important part, and below is the full code.
<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>
<!-- 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" HorizontalContentAlignment="Stretch">
<TreeViewItem Header="test2"/>
<TreeViewItem Header="test2">
<TreeViewItem Header="sub test">
<TreeViewItem Header="sub test1-1"/>
<TreeViewItem Header="sub test1-2"/>
</TreeViewItem>
<TreeViewItem Header="sub test2"/>
</TreeViewItem>
<TreeViewItem Header="test3"/>
</TreeView>
Full TreeViewItem Style
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<!--=================================================================
TreeViewItem
==================================================================-->
<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>
TreeViewItem Header won't stretch?
This problem occurs because WPF's default template for TreeViewItem is set up as a 3-column by 2-row Grid. The first row is for the "header" (actually a Border), and the second row is for the ItemsPresenter. The two rows are made visible or hidden as necessary, in order to accomplish the tree expansion when you click the little triangle--which occupies column zero of the Grid.
Both rows really only need one additional column. For example, in the second row, we must have nothing at col-0, row-1, because that blank part should be indented when IsExpanded is true. But the mystery begins when we note that the ItemsPresenter, based in col-1, row-1, specifies Grid.ColumnSpan=2.
Unfortunately in the top row, the Border which holds the header is set to Grid.Column=1... but no ColumnSpan. Since the col-2 of the Grid has Width=* this means the header/border won't stretch horizontally.
In other words, I seems like the 3-column grid design has no purpose except to specifically to prevent the header from stretching. As far as I can tell, a simple 2x2 arrangement would be more flexible [edit: see footnote #2], and support either full-stretching or the 'jagged' header non-stretching, via the regular WPF alignment mechanisms.
Ideally, we'd change the Grid to have only 2 columns instead of 3. Since that's not so easy, instead we'll make the header span 2 columns, just like the ItemsPresenter does.
Ok, here is a small, complete, self-contained (XAML-only) working program which demonstrates--and fixes--the problem:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/netfx/2007/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:r="clr-namespace:System.Reflection;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Width="800" SizeToContent="Manual">
<TreeView ItemsSource="{Binding Source={StaticResource data}}"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingPanel.ScrollUnit="Item">
<TreeView.Resources>
<ObjectDataProvider x:Key="data" ObjectInstance="{x:Static sys:AppDomain.CurrentDomain}" MethodName="GetAssemblies" />
<HierarchicalDataTemplate DataType="{x:Type r:Assembly}" ItemsSource="{Binding Path=DefinedTypes}" >
<TextBlock Text="{Binding Path=Location}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type sys:Type}" ItemsSource="{Binding Path=CustomAttributes}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type r:CustomAttributeData}" ItemsSource="{Binding Path=ConstructorArguments}">
<TextBlock Text="{Binding Path=AttributeType.Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- == == BEGIN HERE == == -->
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Grid.ColumnSpan" Value="2" />
</Style>
</Style.Resources>
<!-- == == == END == == == -->
<Setter Property="Background" Value="LightBlue" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Window>
If you run this program as shown you will see something like this. This is the altered (modified) behavior, which allows you to regain full control over the stretching behavior of the TreeViewItem header:
Notice the BEGIN/END part with the dotted lines in the XAML source. Basically, I just set Grid.ColumnSpan=2 on the offending Border, so that it will fill the stretched width of the Grid. That element is emitted by the TreeViewItem template, so I found that an effective way to alter its properties is via a targeting Style in the resource dictionary of the TreeViewItem's Style. Yes, confusing. That Style is accessed via TreeViewItem.ItemContainerStyle.
To see the (existing) broken behavior, you can comment out the part between the dotted lines:
You can also set these styles in some resource dictionary, rather than using the ItemContainerStyle property as I did here. I did it this way because it minimizes the scope of the fix, so that unrelated Border controls won't be affected. If you do need a more discriminative way to target just this control, you might be able to take advantage of the fact that it has Name='Bd'.
[edit:] This solution does not use reflection! Don't be scared by the meaningless demo data--it has nothing to do with this issue; it was just the easiest way to grab some hierarchical data for demonstration purposes, while keeping the entire program tiny.
[edit #2:] I just realized that what the designers were trying to avoid with the 3x2 grid arrangement was the following unsightly effect (exaggerated here by a zoomed-out screenshot). So if you adopt one of the solutions from this page, be forewarned that you might not want this:
If you mean something like this screenshot
(source: bendewey.com)
Update
As noted this example has the downfall of being indented on the subitems
(source: bendewey.com)
Then this should help you. Its also based on http://msdn.microsoft.com/en-us/library/ms788727.aspx you can change the TreeViewItem's Template to a StackPanel and set the ItemsPanel left Margin to 19. Then in the TreeView you set the HorizontalContentAlignment="Stretch". I'm attaching the entire resource below, but here is the important part.
<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>
Control
<TreeView Margin="50" HorizontalContentAlignment="Stretch">
<TreeViewItem Header="test2"/>
<TreeViewItem Header="test2">
<TreeViewItem Header="sub test"/>
<TreeViewItem Header="sub test2"/>
</TreeViewItem>
<TreeViewItem Header="test3"/>
</TreeView>
Resources
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<!--=================================================================
TreeViewItem
==================================================================-->
<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}">
<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>
<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>
This is by far the easiest solution. Just create a rectangle, call it Hb, and set its margine to -100px and not visible. Only set it to Visible when you've it selected or mouse over. It's a hack, but you're good for up to 5 levels of nested TreeViewItems (100 > 19*5)
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" VerticalAlignment="Top" Panel.ZIndex="1"/>
<Rectangle x:Name="Hb" Width="Auto" Height="Auto" Grid.ColumnSpan="2" Margin="-100,0,0,0" Panel.ZIndex="-1" Visibility="Hidden" />
<Border x:Name="Bd" SnapsToDevicePixels="true" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Column="1" Panel.ZIndex="0">
<ContentPresenter x:Name="PART_Header" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" HorizontalAlignment="Stretch"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="19,0,0,0"/>
</Grid>
If you mean something like this screenshot
(source: bendewey.com)
Then this should help you. Its based on http://msdn.microsoft.com/en-us/library/ms788727.aspx you can make some changes to the TreeViewItem's Grid layout. Basically you remove the third column. Then in the TreeView you set the HorizontalContentAlignment="Stretch". I'm attaching the entire resource below, but here is the important part.
<!-- ... -->
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19"
Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- ... -->
Control
<TreeView Margin="50" HorizontalContentAlignment="Stretch">
<TreeViewItem Header="test2"/>
<TreeViewItem Header="test2">
<TreeViewItem Header="sub test"/>
<TreeViewItem Header="sub test2"/>
</TreeViewItem>
<TreeViewItem Header="test3"/>
</TreeView>
Resources
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<!--=================================================================
TreeViewItem
==================================================================-->
<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}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19"
Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Border Name="Bd"
Grid.Column="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
<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>
Source of the problem when use TreeView with ItemsSource is , refered from link text, I have changed some code of TreeViewItemExtensions class:
public static class TreeViewItemExtensions
{
public static int GetDepth(this TreeViewItem item)
{
while (GetSelectedTreeViewItemParent(item) != null)
{
var parent = GetSelectedTreeViewItemParent(item);
if (parent != null)
return parent.GetDepth() + 1;
item = parent;
}
return 0;
}
public static TreeViewItem GetSelectedTreeViewItemParent(this TreeViewItem item)
{
DependencyObject parent = VisualTreeHelper.GetParent(item);
while (!(parent is TreeViewItem || parent is TreeView))
{
parent = VisualTreeHelper.GetParent(parent);
}
return parent as TreeViewItem;
}
}
Used something like theseven7 to facilitate use of bendewey's code with templated TreeViewItems...
public static int GetDepth(this TreeViewItem item)
{
FrameworkElement elem = item;
var parent = VisualTreeHelper.GetParent(item);
var count = 0;
while (parent != null && !(parent is TreeView))
{
var tvi = parent as TreeViewItem;
if (parent is TreeViewItem)
count++;
parent = VisualTreeHelper.GetParent(parent);
}
return count;
}
For a XAML only approach I've taken one of Bendewey's solutions and broken it up a bit into a more basic solution:
The style below should allow Treeview items to span:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19"
Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander"
Content="..."
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Border Name="Bd" Grid.Column="1" Background="Red" Padding="3">
<ContentPresenter x:Name="PART_Header" ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1"/>
</Grid>
<!-- ADD TRIGGERS HERE -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In order to get it to operate and collapse like a proper Treeview the below triggers should allow this:
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader" Value="false"/>
<Condition Property="Width" Value="Auto"/>
</MultiTrigger.Conditions>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader" Value="false"/>
<Condition Property="Height" Value="Auto"/>
</MultiTrigger.Conditions>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
</MultiTrigger>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Bd" Property="Background" Value="Blue"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsExpanded" Value="false">
<Setter TargetName="ItemsHost"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
Just nest the triggers within the Control Template. Colours/padding/design will need to be tweaked to suit your own needs but the above should be a very basic idea on a XAML only foundation.
I managed this by copying the ItemContainerStyle out using blend, giving a name to the grid that the item is placed in and then setting the background of the grid.

Resources