Example 2: TreeViewItem template - Athari/Alba.Jaml GitHub Wiki
The following template is based on default TreeViewItem
template. It modifies horizontal content alignment to make text wrapping in tree items possible.
JAML (10,024 bytes)
_={
$: 'Window root',
Resources: [{
$: 'Style ExpandCollapseToggleStyle ToggleButton',
set: {
Width: 16, Height: 16, Focusable: false,
Template: {
TargetType: 'ToggleButton',
_: [{
$: 'Border',
Width: 16, Height: 16, Background: 'Transparent', Padding: '3 4 3 3',
_: [{
$: 'Path ExpandPath', Fill: 'Transparent', Stroke: '#FF989898', Data: 'M 4,0 L 8,4 4,8 Z'
}]
}],
on: {
'{=this.IsChecked}': {set: {
'ref.ExpandPath.RenderTransform': {
$: 'RotateTransform', Angle: 45, CenterX: 4, CenterY: 4
},
'ref.ExpandPath.Fill': '#FF595959', 'ref.ExpandPath.Stroke': '#FF262626'
}},
'{=this.IsMouseOver}': {set: {
'ref.ExpandPath.Fill': '#FF1BBBFA', 'ref.ExpandPath.Stroke': 'Transparent'
}},
'{= (bool)${=this.IsMouseOver} && (bool)${=this.IsChecked} }': {set: {
'ref.ExpandPath.Fill': '#FF262626', 'ref.ExpandPath.Stroke': '#FF595959'
}}
}
}
}
}, {
$: 'Style TreeViewItem',
set: {
HorizontalContentAlignment: 'Stretch',
Template: {
TargetType: 'TreeViewItem',
_: [{
$: 'Grid',
ColumnDefinitions: [ { Width: 'Auto', MinWidth: 20 }, { Width: 'Auto' }, { Width: '*' }],
RowDefinitions: [ { Height: 'Auto' }, { Height: 'Auto' } ],
_: [{
$: 'ToggleButton chkExpand', Grid$: '0 0',
ClickMode: 'Press', IsChecked: '{=tpl.IsExpanded}', Style: '{@ExpandCollapseToggleStyle}'
}, {
$: 'Border brdBG', Grid$: '0 1 1 2', Padding: '{tpl.Padding}', Background: '{tpl.Background}',
BorderBrush: '{tpl.BorderBrush}', BorderThickness: '{tpl.BorderThickness}',
_: {
$: 'ContentPresenter PART_Header', ContentSource: 'Header',
HorizontalAlignment: '{tpl.HorizontalContentAlignment}'
}
}, {
$: 'ItemsPresenter preItems', Grid$: '1 1 1 2'
}]
}],
on: {
'{= !(bool)${=this.IsExpanded} }': {set: {
'ref.preItems.Visibility': 'Collapsed'
}},
'{= !(bool)${=this.HasItems} }': {set: {
'ref.chkExpand.Visibility': 'Hidden'
}},
'{=this.IsSelected}': {set: {
'ref.brdBG.Background': '{@={static.SystemColors.HighlightBrushKey}}',
'Foreground': '{@={static.SystemColors.HighlightTextBrushKey}}'
}},
'{= (bool)${=this.IsSelected} && !(bool)${=this.IsSelectionActive} }': {set: {
'ref.brdBG.Background': '{@={static.SystemColors.InactiveSelectionHighlightBrushKey}}',
'Foreground': '{@={static.SystemColors.InactiveSelectionHighlightTextBrushKey}}'
}},
'{= !(bool)${=this.IsEnabled} }': {set: {
'Foreground': '{@={static.SystemColors.GrayTextBrushKey}}'
}}
}
}
}
}, {
$: 'Style TreeView',
set: {
'ScrollViewer.HorizontalScrollBarVisibility': 'Disabled'
}
}],
_: [{
$: 'Grid',
_: [{
$: 'TreeView',
Items: [{
$: 'TreeViewItem', Header: 'Root', Items: [{
$: 'TreeViewItem', Header: 'Item 1', Items: [{
$: 'TreeViewItem', Header: 'Item 1 1'
}, {
$: 'TreeViewItem', Header: 'Item 1 2'
}, {
$: 'TreeViewItem', Header: 'Item 1 3'
}]
}, {
$: 'TreeViewItem', Header: 'Item 2', Items: [{
$: 'TreeViewItem', Header: 'Item 2 1', Items: [{
$: 'TreeViewItem', Header: 'Item 2 1 1'
}]
}]
}, {
$: 'TreeViewItem', Header: 'Item 3'
}, {
$: 'TreeViewItem', Header: 'Item 4'
}, {
$: 'TreeViewItem', Header: 'Item 5'
}]
}]
}]
}]
}
Manually written XAML (16,958 bytes)
<Window x:Class="Alba.JamlTestApp.TreeViewWindow" x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TreeView>
<TreeView.Items>
<TreeViewItem Header="Root">
<TreeViewItem.Items>
<TreeViewItem Header="Item 1">
<TreeViewItem.Items>
<TreeViewItem Header="Item 1 1"/>
<TreeViewItem Header="Item 1 2"/>
<TreeViewItem Header="Item 1 3"/>
</TreeViewItem.Items>
</TreeViewItem>
<TreeViewItem Header="Item 2">
<TreeViewItem.Items>
<TreeViewItem Header="Item 2 1">
<TreeViewItem.Items>
<TreeViewItem Header="Item 2 1 1"/>
</TreeViewItem.Items>
</TreeViewItem>
</TreeViewItem.Items>
</TreeViewItem>
<TreeViewItem Header="Item 3"/>
<TreeViewItem Header="Item 4"/>
<TreeViewItem Header="Item 5"/>
</TreeViewItem.Items>
</TreeViewItem>
</TreeView.Items>
</TreeView>
</Grid>
<Window.Resources>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="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="3 4 3 3">
<Path x:Name="ExpandPath" Fill="Transparent" Stroke="#FF989898"
Data="M 4,0 L 8,4 4,8 Z"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="ExpandPath" Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="45" CenterX="4" CenterY="4"/>
</Setter.Value>
</Setter>
<Setter TargetName="ExpandPath" Property="Fill" Value="#FF595959"/>
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF262626"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF1BBBFA"/>
<Setter TargetName="ExpandPath" Property="Fill" Value="Transparent"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF262626"/>
<Setter TargetName="ExpandPath" Property="Fill" Value="#FF595959"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TreeViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="20"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ToggleButton x:Name="chkExpand" Grid.Column="0" ClickMode="Press"
IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="brdBG" Grid.Column="1" Grid.ColumnSpan="2"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter x:Name="PART_Header" ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<ItemsPresenter x:Name="preItems" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="preItems" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="False">
<Setter TargetName="chkExpand" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="brdBG" 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="brdBG" Property="Background"
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>
<Style TargetType="{x:Type TreeView}">
<Style.Setters>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
</Style.Setters>
</Style>
</Window.Resources>
</Window>
Generated XAML (reformatted for readability) (23,500 bytes)
<Window
x:Name="root"
x:Class="Alba.JamlTestApp.TreeViewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Alba.JamlTestApp">
<Grid>
<TreeView>
<TreeView.Items>
<TreeViewItem Header="Root">
<TreeViewItem.Items>
<TreeViewItem Header="Item 1">
<TreeViewItem.Items>
<TreeViewItem Header="Item 1 1" />
<TreeViewItem Header="Item 1 2" />
<TreeViewItem Header="Item 1 3" />
</TreeViewItem.Items>
</TreeViewItem>
<TreeViewItem Header="Item 2">
<TreeViewItem.Items>
<TreeViewItem Header="Item 2 1">
<TreeViewItem.Items>
<TreeViewItem Header="Item 2 1 1" />
</TreeViewItem.Items>
</TreeViewItem>
</TreeViewItem.Items>
</TreeViewItem>
<TreeViewItem Header="Item 3" />
<TreeViewItem Header="Item 4" />
<TreeViewItem Header="Item 5" />
</TreeViewItem.Items>
</TreeViewItem>
</TreeView.Items>
</TreeView>
</Grid>
<Window.Resources>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
<Style.Setters>
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Width="16" Height="16" Background="Transparent" Padding="3 4 3 3">
<Path x:Name="ExpandPath" Fill="Transparent" Stroke="#FF989898"
Data="M 4,0 L 8,4 4,8 Z" />
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Self}}"
Value="True">
<DataTrigger.Setters>
<Setter TargetName="ExpandPath" Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="45" CenterX="4" CenterY="4" />
</Setter.Value>
</Setter>
<Setter TargetName="ExpandPath" Property="Fill" Value="#FF595959" />
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF262626" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}"
Value="True">
<DataTrigger.Setters>
<Setter TargetName="ExpandPath" Property="Fill" Value="#FF1BBBFA" />
<Setter TargetName="ExpandPath" Property="Stroke" Value="Transparent" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger
Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{x:Static my:TreeViewWindow._jaml_TreeViewWindowConverter}">
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="IsChecked" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter TargetName="ExpandPath" Property="Fill" Value="#FF262626" />
<Setter TargetName="ExpandPath" Property="Stroke" Value="#FF595959" />
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Setters>
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid>
<ToggleButton x:Name="chkExpand" Grid.Row="0" Grid.Column="0" ClickMode="Press"
IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ExpandCollapseToggleStyle}" />
<Border x:Name="brdBG" Grid.Row="0" Grid.Column="1" Grid.RowSpan="1" Grid.ColumnSpan="2"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter x:Name="PART_Header" ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Border>
<ItemsPresenter x:Name="preItems" Grid.Row="1" Grid.Column="1"
Grid.RowSpan="1" Grid.ColumnSpan="2" />
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="20" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger
Binding="{Binding IsExpanded, RelativeSource={RelativeSource Self},
Converter={x:Static my:TreeViewWindow._jaml_TreeViewWindowConverter1}}"
Value="True">
<DataTrigger.Setters>
<Setter TargetName="preItems" Property="Visibility" Value="Collapsed" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger
Binding="{Binding HasItems, RelativeSource={RelativeSource Self},
Converter={x:Static my:TreeViewWindow._jaml_TreeViewWindowConverter2}}"
Value="True">
<DataTrigger.Setters>
<Setter TargetName="chkExpand" Property="Visibility" Value="Hidden" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger
Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}"
Value="True">
<DataTrigger.Setters>
<Setter TargetName="brdBG" Property="Background"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger
Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{x:Static my:TreeViewWindow._jaml_TreeViewWindowConverter3}">
<Binding Path="IsSelected" RelativeSource="{RelativeSource Mode=Self}" />
<Binding Path="IsSelectionActive" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter TargetName="brdBG" Property="Background" Value="{DynamicResource {x:Static
SystemColors.InactiveSelectionHighlightBrushKey}}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static
SystemColors.InactiveSelectionHighlightTextBrushKey}}" />
</DataTrigger.Setters>
</DataTrigger>
<DataTrigger
Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self},
Converter={x:Static my:TreeViewWindow._jaml_TreeViewWindowConverter4}}"
Value="True">
<DataTrigger.Setters>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<Style TargetType="{x:Type TreeView}">
<Style.Setters>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
</Style.Setters>
</Style>
</Window.Resources>
</Window>
Generated CS (unnecessary usings removed)
// ReSharper disable RedundantUsingDirective
// ReSharper disable RedundantCast
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace Alba.JamlTestApp
{
public partial class TreeViewWindow
{
public static IMultiValueConverter _jaml_TreeViewWindowConverter = new _jaml_TreeViewWindowConverter_Class();
public static IValueConverter _jaml_TreeViewWindowConverter1 = new _jaml_TreeViewWindowConverter1_Class();
public static IValueConverter _jaml_TreeViewWindowConverter2 = new _jaml_TreeViewWindowConverter2_Class();
public static IMultiValueConverter _jaml_TreeViewWindowConverter3 = new _jaml_TreeViewWindowConverter3_Class();
public static IValueConverter _jaml_TreeViewWindowConverter4 = new _jaml_TreeViewWindowConverter4_Class();
private class _jaml_TreeViewWindowConverter_Class : IMultiValueConverter
{
public object Convert (object[] values, Type targetType, object param, CultureInfo culture)
{
if (values.Any(v => ReferenceEquals(v, DependencyProperty.UnsetValue)))
return DependencyProperty.UnsetValue;
return (bool)(values[0]) && (bool)(values[1]);
}
public object[] ConvertBack (object value, Type[] targetTypes, object param, CultureInfo culture)
{
throw new NotSupportedException("Converter supports only one-way binding.");
}
}
private class _jaml_TreeViewWindowConverter1_Class : IValueConverter
{
public object Convert (object value, Type targetType, object param, CultureInfo culture)
{
if (ReferenceEquals(value, DependencyProperty.UnsetValue))
return DependencyProperty.UnsetValue;
return !(bool)value;
}
public object ConvertBack (object value, Type targetType, object param, CultureInfo culture)
{
throw new NotSupportedException("Converter supports only one-way binding.");
}
}
private class _jaml_TreeViewWindowConverter2_Class : IValueConverter
{
public object Convert (object value, Type targetType, object param, CultureInfo culture)
{
if (ReferenceEquals(value, DependencyProperty.UnsetValue))
return DependencyProperty.UnsetValue;
return !(bool)value;
}
public object ConvertBack (object value, Type targetType, object param, CultureInfo culture)
{
throw new NotSupportedException("Converter supports only one-way binding.");
}
}
private class _jaml_TreeViewWindowConverter3_Class : IMultiValueConverter
{
public object Convert (object[] values, Type targetType, object param, CultureInfo culture)
{
if (values.Any(v => ReferenceEquals(v, DependencyProperty.UnsetValue)))
return DependencyProperty.UnsetValue;
return (bool)(values[0]) && !(bool)(values[1]);
}
public object[] ConvertBack (object value, Type[] targetTypes, object param, CultureInfo culture)
{
throw new NotSupportedException("Converter supports only one-way binding.");
}
}
private class _jaml_TreeViewWindowConverter4_Class : IValueConverter
{
public object Convert (object value, Type targetType, object param, CultureInfo culture)
{
if (ReferenceEquals(value, DependencyProperty.UnsetValue))
return DependencyProperty.UnsetValue;
return !(bool)value;
}
public object ConvertBack (object value, Type targetType, object param, CultureInfo culture)
{
throw new NotSupportedException("Converter supports only one-way binding.");
}
}
}
}