How to create object of wpf control from View model - wpf

Hi I have created converter to change the behaviour of Treeview control and this is working fine.this is my code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DevLake.OrgChart.UI.View
{
/// <summary>
/// Interaction logic for TreeView.xaml
/// </summary>
public partial class TreeView : UserControl
{
public TreeView()
{
InitializeComponent();
}
}
class TreeViewLineConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
TreeViewItem item = (TreeViewItem)value;
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);
return ic.ItemContainerGenerator.IndexFromContainer(item) == ic.Items.Count - 1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
}
But now I want move this code behind in to view model.But I am not able to create the object of TreeViewItem from view model.Please help me how do this in MVVM pattern
my UserControl:
<UserControl x:Class="DevLake.OrgChart.UI.View.TreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DevLake.OrgChart.UI.View"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<UserControl.Resources>
<local:TreeViewLineConverter x:Key="LineConverter"/>
<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="White">
<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="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Connecting Lines -->
<Rectangle x:Name="HorLn" Height="1" Stroke="#8888" Margin="10,0,0,0" SnapsToDevicePixels="true"/>
<Rectangle x:Name="VerLn" Width="1" Stroke="#8888" Grid.RowSpan="2" SnapsToDevicePixels="true"/>
<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.ColumnSpan="2"/>
</Grid>
<ControlTemplate.Triggers>
<!-- This trigger changes the connecting lines if the item is the last in the list -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LineConverter}}" Value="true">
<Setter TargetName="VerLn"
Property="Height"
Value="6"/>
<Setter TargetName="VerLn"
Property="VerticalAlignment"
Value="Top"/>
</DataTrigger>
<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>
</UserControl.Resources>
<StackPanel>
<TreeView x:Name="tvMain" ItemsSource="{Binding Root}" BorderThickness="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}" MaxHeight="40" MaxWidth="40" Margin="2"/>
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
<MultiBinding StringFormat=" {0} {1}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="true">
<Setter Property="StackPanel.Background" Value="LightBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</TreeView.ItemContainerStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SelectedCommand}"
CommandParameter="{Binding ElementName=tvMain, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
</StackPanel>
</UserControl>

Related

Show ContextMenu on whole TreeViewItem line in WPF

I have a TreeView with heterogeneuos node types. Each node type is configuered in XAML using a HierarchicalDataTemplate.
Some of the nodes have a ContextMenu, which is dependent on the type of the node. The ContextMenus are defined as static resources in XAML and attached to a DockPanel in the HierarchicalDataTemplate.
In addition, I am using the ControlTemplate for a TreeViewItem described by bendewey in the following StackOverflow question https://stackoverflow.com/a/672123/1626109. This ControlTemplate is defined so that the complete TreeViewItem is highlighted when it is selected.
A left click on any part of the line, selects the item.
In contrast, the context menus, which are defined in the HierarchicalDataTemplate, only work on the right hand part of the line.
I am looking for a way to configure the ContextMenus so that they are also available on the complete line.
This seems to require attaching the context menu to an element in the ControlTemplate, with a TemplateBinding to something defined in the HierarchicalDataTemplate, but I can't figure out how to do it.
By the way, the solution explorer in Visual Studio has exactly this behavior. The context menu is dependent on the node type, and it is available on the complete item, including to the left of the expand/collapse button.
(Edited) You need to get rid of the two columns of the Grid in TreeViewItem Style:
<Window.Resources>
<local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid Width="15" Height="13"
Background="Transparent">
<Path x:Name="ExpandPath"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="1,1,1,1"
Fill="{StaticResource GlyphBrush}"
Data="M 4 0 L 8 4 L 4 8 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Data"
TargetName="ExpandPath"
Value="M 0 4 L 8 4 L 4 8 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="0,0,0,0"
StrokeThickness="5"
Stroke="Black"
StrokeDashArray="1 2"
Opacity="0"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type TreeViewItem}"
TargetType="{x:Type TreeViewItem}">
<Setter Property="Background"
Value="Transparent"/>
<Setter Property="HorizontalContentAlignment"
Value="{Binding Path=HorizontalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment"
Value="{Binding Path=VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding"
Value="1,0,0,0"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FocusVisualStyle"
Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<ControlTemplate.Resources>
<local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
</ControlTemplate.Resources>
<StackPanel>
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<Grid>
<ToggleButton Panel.ZIndex="2" x:Name="Expander"
HorizontalAlignment="Left"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"
Margin="{Binding Converter={StaticResource lengthConverter}, ConverterParameter=0,
RelativeSource={RelativeSource TemplatedParent}}"/>
<ContentPresenter x:Name="PART_Header" ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
</ContentPresenter>
</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>
</Window.Resources>
However, this will distort the Header-Items arrangements. Therefore, you need to adjust the Margin in the HierarchicalDataTemplate part:
<TreeView Margin="50" HorizontalContentAlignment="Stretch" DataContext="{Binding}" ItemsSource="{Binding Models}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Model}" ItemsSource="{Binding Models}">
<Border Background="Transparent">
<TextBlock Margin="{Binding Converter={StaticResource lengthConverter}, ConverterParameter=1,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TreeViewItem}}"
Text="{Binding Name}" />
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="dddd"/>
</ContextMenu>
</Border.ContextMenu>
</Border>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Note that you should adjusted the converter to take the required extra margin into account:
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);
int extra = int.Parse(parameter.ToString());
var t = item.GetDepth();
return new Thickness(Length * (item.GetDepth() + extra), 0, 0, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
If converter parameter is 1, it will add one point to the depth of the item. I use it for the Header part.
Another approach might be to add a DataTemplate to the ContentPresenter of the TreeViewItem Style. While I don't know how you can bind the ContextMenu, I prefer this one.

Not able to change the wpf default node linking line for treeview control

I am trying to change the default node linking line for wpf treeview HierarchicalDataTemplate with help of converter.But still I am not able change the default look of tree view node linking .I tried to achieve this with help of below resource code but it is not changing the node default look even though it does not throw any error . Please help me to change the default look of node linking .I want to show the node linking with line
<UserControl x:Class="DevLake.OrgChart.UI.View.TreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DevLake.OrgChart.UI.View"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<UserControl.Resources>
<local:TreeViewLineConverter x:Key="LineConverter"/>
<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="White">
<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="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Connecting Lines -->
<Rectangle x:Name="HorLn" Height="1" Stroke="#8888" Margin="10,0,0,0" SnapsToDevicePixels="true"/>
<Rectangle x:Name="VerLn" Width="1" Stroke="#8888" Grid.RowSpan="2" SnapsToDevicePixels="true"/>
<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.ColumnSpan="2"/>
</Grid>
<ControlTemplate.Triggers>
<!-- This trigger changes the connecting lines if the item is the last in the list -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LineConverter}}" Value="true">
<Setter TargetName="VerLn"
Property="Height"
Value="6"/>
<Setter TargetName="VerLn"
Property="VerticalAlignment"
Value="Top"/>
</DataTrigger>
<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>
</UserControl.Resources>
<StackPanel>
<TreeView x:Name="tvMain" ItemsSource="{Binding Root}" BorderThickness="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}" MaxHeight="40" MaxWidth="40" Margin="2"/>
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
<MultiBinding StringFormat=" {0} {1}">
<Binding Path="FirstName"/>
<Binding Path="LastName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="true">
<Setter Property="StackPanel.Background" Value="LightBlue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</TreeView.ItemContainerStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SelectedCommand}"
CommandParameter="{Binding ElementName=tvMain, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
</StackPanel>
</UserControl>
and my converter code is :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DevLake.OrgChart.UI.View
{
/// <summary>
/// Interaction logic for TreeView.xaml
/// </summary>
public partial class TreeView : UserControl
{
public TreeView()
{
InitializeComponent();
}
}
class TreeViewLineConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
TreeViewItem item = (TreeViewItem)value;
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);
return ic.ItemContainerGenerator.IndexFromContainer(item) == ic.Items.Count - 1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
}
you have an overriden TreeView.ItemContainerStyle. use BasedOn property to apply a base style where lines are defined
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</TreeView.ItemContainerStyle>

Add a border around all children of a TreeViewItem

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>();
}
}
}

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 - Setting different ToggleButton image for each TreeViewItem root node with styles

I am new to using styles, resources and templates in WPF. What I need to do is override the ToggleButton +/- in the TreeView to be an image, with a different image for each TreeViewItem root Node. For Instance, I would want an image of a car for the "Car" node, and an image of an airplane for the "Plane" node. I have a colorful and a grayscale image of each (for expanded/collapsed).
I've found styles to override the treeview and get the image set for the toggle button, but I am not sure the best way to style each item differently.
the style code for an item is quite long, so I'm pretty sure there's a better way than copy/pasting the full style just to change the source property.
Could someone please point me in the right direction on the best way to do this?
Thank you.
Here's the style I've been playing with, which was copied from a different post and altered for my images.
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle/>
</ControlTemplate>
</Setter.Value>
</Setter>
</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 Width="16" Height="16" Background="Transparent">
<Border Width="16" Height="16" SnapsToDevicePixels="true" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" BorderThickness="1">
<Image x:Name="ExpandImg" Width="16" Height="16" Source="/MyApp;component/Images/Icons/Grayscale/car.ico" />
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" TargetName="ExpandImg" Value="/MyApp;component/Images/Icons/Color/car.ico"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TreeViewItemStyle1" TargetType="{x:Type TreeViewItem}">
<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="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}" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"/>
<Border x:Name="Bd" SnapsToDevicePixels="true" Grid.Column="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="PART_Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.ColumnSpan="2" 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>
Well, unfortunately, that's the way WPF works. If you want to change the Style of the default controls, you're most likely bound to work with a bunch of Style declarations (as you've posted above) and make some changes on it.
Using Expression Blend makes it a bit easier since you'll have a UI designer that you can use to easily modify your styles. But behind the scenes, it still has the Style declarations. As an example, here's what I got doing your requirements using Blend:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<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">
<Image x:Name="PART_Image" Source="Image1.jpg"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False"/>
<Trigger Property="IsMouseOver" Value="True"/>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" TargetName="PART_Image" Value="Image2.jpg"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TreeViewItem}">
<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="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" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</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>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<TreeView HorizontalAlignment="Left" Margin="8,8,0,105" Width="196">
<TreeViewItem Header="Item1">
<TreeViewItem Header="Item2"/>
</TreeViewItem>
</TreeView>
</Grid>
</Window>
Anyway, I hope this helps.
EDIT:
Ok, I noticed that the code I posted is much like what you posted in your example. But I assure you, I used expression blend to make the modifications above. And I guess that just proves the point: you're bound to work with a bunch of Style declarations, whether using Blend or not. =)
EDIT2:
Window.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="300" Width="300">
<Window.Resources>
<HierarchicalDataTemplate x:Key="treeTemplate"
ItemsSource="{Binding SubItems}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
<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" Width="16">
<Image x:Name="PART_Image" Source="{Binding ImageSource}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="customTreeItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<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="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" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</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>
</Window.Resources>
<TreeView x:Name="tree"
ItemTemplate="{StaticResource treeTemplate}"
ItemContainerStyle="{StaticResource customTreeItemStyle}"/>
</Window>
Code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private List<ViewModel> items = new List<ViewModel>();
public MainWindow()
{
InitializeComponent();
ViewModelImpl1 vm1 = new ViewModelImpl1() { Name = "VM1" };
vm1.SubItems.Add("SubItem1");
vm1.SubItems.Add("SubItem2");
vm1.SubItems.Add("SubItem3");
ViewModelImpl1 vm2 = new ViewModelImpl1() { Name = "VM2" };
vm2.SubItems.Add("SubItem1");
vm2.SubItems.Add("SubItem2");
vm2.SubItems.Add("SubItem3");
vm2.SubItems.Add("SubItem4");
ViewModelImpl2 vm3 = new ViewModelImpl2() { Name = "VM3" };
vm3.SubItems.Add("SubItem1");
items.Add(vm1);
items.Add(vm2);
items.Add(vm3);
tree.ItemsSource = items;
}
}
}
ViewModels
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication2
{
public abstract class ViewModel : INotifyPropertyChanged
{
private List<string> subItems = new List<string>();
public List<string> SubItems
{
get { return this.subItems; }
}
private string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
this.isExpanded = value;
this.OnPropertyChanged("IsExpanded");
UpdateImageSource();
}
}
private string imageSource;
public string ImageSource
{
get
{
if (this.imageSource == null)
UpdateImageSource();
return this.imageSource;
}
protected set
{
this.imageSource = value;
this.OnPropertyChanged("ImageSource");
}
}
protected abstract void UpdateImageSource();
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ViewModelImpl1 : ViewModel
{
protected override void UpdateImageSource()
{
if (this.IsExpanded)
this.ImageSource = "pack://application:,,,/Images/bell1.png";
else
this.ImageSource = "pack://application:,,,/Images/bell2.png";
}
}
public class ViewModelImpl2 : ViewModel
{
protected override void UpdateImageSource()
{
if (this.IsExpanded)
this.ImageSource = "pack://application:,,,/Images/star1.png";
else
this.ImageSource = "pack://application:,,,/Images/star2.png";
}
}
}

Resources