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:
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!!!
