0

I am working on a project where I have a PowerShell driven UI that has a Treeview list with one selection. I have that code working just fine following this blog PowerShell WPF - Customize TreeView Icon...great article BTW! I would like to customize the tree list with different images for different files base on its item tag. I can get this to work using png image source and DataTriggers, however I am trying to embed all images as SVG within a canvas template. The only way I know how to get this to work is using the VisualBrush element but I cannot seem to get this to work within the Treeview.resource framework element. Here is what I have so far:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TreeView"
        Height="600" Width="800"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterScreen"
        ShowInTaskbar="False" Topmost="True">  
    <Window.Resources>
        
        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Resources\Icons.xaml" />
                    <ResourceDictionary Source="Resources\TabControl_LeftSideStyle.xaml" />
                    <ResourceDictionary Source="Resources\TreeViewItem_StandardStyle.xaml" />

                </ResourceDictionary.MergedDictionaries>
            
            
            <Style TargetType="{x:Type Window}">
                <Setter Property="FontFamily" Value="Segoe UI" />
                <Setter Property="FontWeight" Value="Light" />
                <Setter Property="BorderBrush" Value="#004275" />
                <Setter Property="BorderThickness" Value="0.5" />
            </Style>

            <HierarchicalDataTemplate x:Key="CheckBoxItemTemplate" ItemsSource="{Binding Children, Mode=OneTime}">
                <StackPanel Orientation="Horizontal">
                    <CheckBox Focusable="False" IsChecked="{Binding IsChecked}" VerticalAlignment="Center" />
                    <ContentPresenter Content="{Binding Name, Mode=OneTime}" Margin="2,0" />
                </StackPanel>
            </HierarchicalDataTemplate>
            </ResourceDictionary>
        </Window.Resources>
    
    <Grid>
        
        <Rectangle Fill="#004275" HorizontalAlignment="Left" Height="600" VerticalAlignment="Center" Width="150"/>
        <Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Right" Height="600" VerticalAlignment="Center" Width="156"/>
        <StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10">
            <Image x:Name="_wizMainLogo" Height="48" Width="132" Stretch="Uniform" />
        </StackPanel>

        <!-- START TAB MENU -->
        <TabControl x:Name="_wizTabControl" Style="{DynamicResource TabControlLeftSide}" Width="800" Height="550" HorizontalAlignment="Center" VerticalAlignment="Top">
            <TabItem x:Name="_wizid" Header="Tree view" Style="{DynamicResource TabItemsWhite}" Width="150" Margin="0" IsEnabled="False">
                <Grid x:Name="TabLayout" Margin="0" Grid.ColumnSpan="2" FocusManager.FocusedElement="{Binding ElementName=tsTree}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="490"></ColumnDefinition>
                        <ColumnDefinition Width="150"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <Label x:Name="TabMainTitle" Grid.Column="0" HorizontalAlignment="Left" Margin="10,20,0,0" VerticalAlignment="Top" FontSize="22" Content="Tree view list with icons"/>
                    <Rectangle Grid.Column="0" Fill="HotPink" HorizontalAlignment="Right" Height="40" Width="40" Margin="0,20,13.5,0" VerticalAlignment="Top">
                        <Rectangle.OpacityMask>
                            <VisualBrush Stretch="Fill" Visual="{DynamicResource icons_listoutline}"/>
                        </Rectangle.OpacityMask>
                    </Rectangle>


                    <Label x:Name="TabSubTitle" FontSize="14" HorizontalAlignment="Left" Margin="10,73,0,0" VerticalAlignment="Top" Content="Search for item in list"/>
                    <TextBox x:Name="TabSearch" HorizontalAlignment="Left" Height="31" Margin="10,103,0,0" TextWrapping="Wrap" Text="Search..." VerticalAlignment="Top" Width="331" Foreground='Gray' VerticalContentAlignment="Center" FontSize="18"/>
                    <Button x:Name="TabSearchClear" Content="Clear" Height="31" Width="63" HorizontalAlignment="Left" VerticalAlignment="Bottom" FontSize="10" Padding="2" Margin="414,0,0,416" />
                    <Button x:Name="TabSearchEnter" Content="Search" Height="31" Width="63" HorizontalAlignment="Left" VerticalAlignment="Bottom" FontSize="10" Padding="2" Margin="346,0,0,416" />

                    <Button x:Name="TabExpand" Content="Expand All" Height="25" Width="113" HorizontalAlignment="Left" VerticalAlignment="Bottom" FontSize="10" Padding="2" Margin="10,0,0,386" />
                    <Button x:Name="TabCollapse" Content="Collapse All" Height="25" Width="113" HorizontalAlignment="Left" VerticalAlignment="Bottom" FontSize="10" Padding="2" Margin="128,0,0,386" />

                    <TreeView x:Name="TabTree" HorizontalAlignment="Left" Height="371" Margin="10,169,0,0" VerticalAlignment="Top" Width="467" FontSize="14"  
                                ItemContainerStyle="{StaticResource TreeViewItemStandard}" 
                                ItemTemplate="{StaticResource CheckBoxItemTemplate}" >
                        <TreeView.Resources>
                            <Style TargetType="{x:Type TreeViewItem}">
                                <Setter Property="HeaderTemplate">
                                    <Setter.Value>
                            
                                        <DataTemplate>
                                            <StackPanel Orientation="Horizontal">
                                              <Image Name="TreeNodeIMG" Width="20" Height="20" Stretch="Fill">
                                                <Image.Style>
                                                  <Style TargetType="{x:Type Image}">
                                                    <Style.Triggers>
                                                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=Tag[0]}" Value="folder">
                                                            <Setter Property="Source" Value="images\folder.png"/>
                                                        </DataTrigger>
                                                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=Tag[0]}" Value="file">
                                                            <Setter Property="Source" Value="images\file.png"/>
                                                        </DataTrigger>
      
                                                    </Style.Triggers>
                                                  </Style>
                                                </Image.Style>
                                              </Image>
                                              <TextBlock VerticalAlignment="Center" Text="{Binding}" Margin="5,0" />
                                            </StackPanel>
                                          </DataTemplate>

                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </TreeView.Resources> 

                    </TreeView>

                    <Label Content="More Info" Grid.Column="1" FontSize="14" HorizontalAlignment="Left" Margin="10,31,0,0" VerticalAlignment="Top" Foreground="LightSlateGray" />
                    <TextBlock x:Name="TabMoreInfo" Grid.Column="1" HorizontalAlignment="Left" Margin="10,89,0,0" Width="136" TextWrapping="Wrap" VerticalAlignment="Top" Height="422">
                        <Run Text="orem ipsum dolor sit amet, consectetur adipiscing elit. Morbi risus mi, consequat in ultricies eu, euismod nec eros. Pellentesque vel augue sed enim euismod sodales varius a ipsum. "/>
                    </TextBlock>
                </Grid>
            </TabItem>
        </TabControl>
        
    </Grid>
    
</Window>

And the PowerShell script sample I am testing with is this:

#$ErrorActionPreference='Stop'
##*=============================================
##* VARIABLES
##*=============================================
[string]$ResourceRoot = ($PWD.ProviderPath, $PSScriptRoot)[[bool]$PSScriptRoot]


##*=============================================
##* LOAD UI
##*=============================================
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null

function LoadXaml ($filename){
    $XamlLoader=(New-Object System.Xml.XmlDocument)
    $XamlLoader.Load($filename)
    return $XamlLoader
}
$XamlMainWindow=LoadXaml($ResourceRoot+"\MainWindow.xaml")
$reader = (New-Object System.Xml.XmlNodeReader $XamlMainWindow)
$Form = [Windows.Markup.XamlReader]::Load($reader)


$Global:FolderTree = $Form.FindName("TabTree")

##*=============================================
##* MAIN
##*=============================================

$dummyNode = $null
$preselect = 'rootFile1.txt'


$AllFiles  = [IO.Directory]::GetFiles("$ResourceRoot\Test")
$AllDirectory = [IO.Directory]::GetDirectories("$ResourceRoot\Test")

# ================== Handle Folders ===========================
#$folder = $AllDirectory[0]
foreach ($folder in $AllDirectory){

    $treeViewItem = [Windows.Controls.TreeViewItem]::new()
    $treeViewItem.Header = $folder.Substring($folder.LastIndexOf("\") + 1)
    $treeViewItem.Tag = @("folder",$folder)
    $treeViewItem.Items.Add($dummyNode) | Out-Null

    
    $treeViewItem.Add_Expanded({
        Write-Host $_.OriginalSource.Header  " is expanded"
        TreeExpanded($_.OriginalSource)
    })
    $FolderTree.Items.Add($treeViewItem)| Out-Null

}

# ================== Handle Files ===========================
foreach ($file in $AllFiles){

    $treeViewItem = [Windows.Controls.TreeViewItem]::new()
    $treeViewItem.Header = $file.Substring($file.LastIndexOf("\") + 1)
    $treeViewItem.Tag = @("file",$file) 
    $FolderTree.Items.Add($treeViewItem)| Out-Null
    If($treeViewItem.Header -eq $preselect){
        $treeViewItem.IsSelected = $true
        Write-Host ("pre selected Item: {0}" -f $treeViewItem.Header)
    }Else{
        Write-Host ("Added Item: {0}" -f $treeViewItem.Header)
    }

    $treeViewItem.Add_PreviewMouseLeftButtonDown({
        [System.Windows.Controls.TreeViewItem]$sender = $args[0]
        [System.Windows.RoutedEventArgs]$e = $args[1]
        Write-Host "Left Click: $($sender.Tag)"
    })

    $treeViewItem.Add_PreviewMouseRightButtonDown({
        [System.Windows.Controls.TreeViewItem]$sender = $args[0]
        [System.Windows.RoutedEventArgs]$e = $args[1]
        Write-Host "Right Click: $($sender.Tag)"
    })

}


Function TreeExpanded($sender){
    
    $global:item = [Windows.Controls.TreeViewItem]$sender
    
    If ($item.Items.Count -eq 1 -and $item.Items[0] -eq $dummyNode)
    {
        $item.Items.Clear();
        Try
        {
            
            foreach ($string in [IO.Directory]::GetDirectories($item.Tag[1].ToString()))
            {
                $subitem = [Windows.Controls.TreeViewItem]::new();
                $subitem.Header = $string.Substring($string.LastIndexOf("\") + 1)
                $subitem.Tag = @("folder",$string)
                $subitem.Items.Add($dummyNode)
                
                $subitem.Add_Expanded({
                    TreeExpanded($_.OriginalSource)
                })
                $item.Items.Add($subitem) | Out-Null
            }

            foreach ($file in [IO.Directory]::GetFiles($item.Tag[1].ToString())){

                $subitem = [Windows.Controls.TreeViewItem]::new()
                $subitem.Header = $file.Substring($file.LastIndexOf("\") + 1)
                $subitem.Tag = @("file",$file) 
                $item.Items.Add($subitem)| Out-Null

                If($subitem.Header -eq $preselect){
                    $subitem.IsSelected = $true
                    Write-Host ("pre-selected Item: {0}" -f $subitem.Header)
                }Else{
                    Write-Host ("Added Item: {0}" -f $subitem.Header)
                }


                $subitem.Add_PreviewMouseLeftButtonDown({
                    [System.Windows.Controls.TreeViewItem]$sender = $args[0]
                    [System.Windows.RoutedEventArgs]$e = $args[1]
                    Write-Host "Left Click: $($sender.Tag)"
                })
            }
        }   
        Catch [Exception] { }
    }
     
}

##*=============================================
##* SHOW UI
##*=============================================

$Form.ShowDialog() | Out-Null

When I run the code it looks like this:

Treeview WPF

This is an issue with the images not showing for the root level folder but not too big of a deal unless someone has an answer for that too...

The canvas resource dictionary file has this in it:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas x:Key="icon_file" Width="24" Height="24">
        <Path Stretch="Fill" Fill="White" Data="M3 5V19H20V5H3M7 7V9H5V7H7M5 13V11H7V13H5M5 15H7V17H5V15M18 17H9V15H18V17M18 13H9V11H18V13M18 9H9V7H18V9Z" />
    </Canvas>

    <Canvas x:Key="icon_folder" Width="24" Height="24">
        <Path Width="20" Height="20" Fill="White" Stretch="Uniform" Data="M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z"/>
    </Canvas>

</ResourceDictionary>

The only way I got it to sort of work is by using this code within the xaml

<TreeView.Resources>
    <Style TargetType="{x:Type TreeViewItem}">
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">

                        <Rectangle Fill="Green" HorizontalAlignment="Right" Height="20" Width="20" VerticalAlignment="Top">
                            <Rectangle.OpacityMask>
                                <VisualBrush Stretch="Fill" Visual="{DynamicResource icons_folder}"/>
                            </Rectangle.OpacityMask>
                        </Rectangle>

                        <TextBlock Text="{Binding}" Margin="5,0" />
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</TreeView.Resources>

Even though this fills the entire image to the fill color, it does show but its the same for both folders and files. Now If I try to use the bindings for the canvas images and the datatriggers, I get a data type error. I have found nothing online that talks about how to use svg data bindings with treeview and data triggers. There is allot out there for C# coding but looking for a PowerShell and .Net solution.

Even though I assumed it would be easier to accomplish this in the XAML code, I did try to programmatically change images within PowerShell...but that too failed miserably. Any ideas?

I need some of your expert skills! THANKS!!!

1 Answer 1

0

You could replace the Image in the HeaderTemplate with a ContentControl that displays one of your Canvas resources:

<Style TargetType="{x:Type TreeViewItem}">
    <Setter Property="HeaderTemplate">
        <Setter.Value>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <ContentControl>
                        <ContentControl.Style>
                            <Style TargetType="ContentControl">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=Tag[0]}" Value="folder">
                                        <Setter Property="Content" Value="{DynamicResource icon_folder}" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=Tag[0]}" Value="file">
                                        <Setter Property="Content" Value="{DynamicResource icon_file}" />
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </ContentControl.Style>
                    </ContentControl>
                    <TextBlock VerticalAlignment="Center" Text="{Binding}" Margin="5,0" />
                </StackPanel>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.