Adding an Image to a TreeViewItem

By Michael Detras

Here's a simple way of adding an image to a TreeViewItem using an attached dependency property to specify the image location.

To add an Image control to a TreeViewItem, we have to modify the style. The following code snippet shows the XAML of the window containing a modified TreeViewItem style along with the other resources it needs. The highlighted part is the Image control.

<Window x:Class="WpfApplication1.MainWindow"
         
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         
xmlns:local="clr-namespace:WpfApplication1"
         
Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
         <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="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"/>
                            <Image x:Name="Image"
                                   Grid.Column="1"
                                   Source="{Binding Path=(local:ImageLocation.Source), RelativeSource={RelativeSource TemplatedParent}}"/>

                            <Border Name="Bd"
                                Grid.Column="2"
                                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="2"
                                           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>
    </Window.Resources>
     <Grid>
        <TreeView>
            <TreeViewItem local:ImageLocation.Source="Test.png" Header="Root">
                 <TreeViewItem Header="Node1"/>
                <TreeViewItem local:ImageLocation.Source="Test.png" Header="Node2"/>
            </TreeViewItem>
        </TreeView>
    </Grid>
</Window>

The definition of the attached dependency property is simple.

namespace WpfApplication1
{
    public class ImageLocation
    {
         public static readonly DependencyProperty SourceProperty = DependencyProperty.RegisterAttached(
          "Source",
          typeof(string),
          typeof(ImageLocation)
        );

        public static void SetSource(UIElement element, string value)
        {
             element.SetValue(SourceProperty, value);
        }

         public static string GetSource(UIElement element)
        {
             return (string)element.GetValue(SourceProperty);
        }
    }
}

Related FAQs

Binding to a an attached dependency property in XAML is not the exactly the same as binding a regular dependency property. It requires the use of parentheses in the binding path.
Adding an Image to a TreeViewItem  (2166 Views)