PowerShell XAML | Update Style Programmatically Failing - wpf

I'm writing a simple automation tool that flips the color of a button using the button styles, but in PowerShell the button just resets back to Windows default style. I've tried googling, but I keep finding answers that don't simply work anymore (likely due to newer PS5).
Here is my code for both:
PowerShell
## v0.09 New CORE ##############
################################
$dev = $true
$ver = 0.09
$DIR_APP = $PSScriptRoot
cls
## Console
################################################################
$Script:showWindowAsync = Add-Type -MemberDefinition #"
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
"# -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru
if(!$DEV){ $null = $showWindowAsync::ShowWindowAsync((Get-process -Id $pid).MainWindowHandle, 0); }
function Get-KeyState([uint16]$k)
{
$s = '[DllImport("user32.dll")]public static extern short GetKeyState(int nVirtKey);'
$t = Add-Type -MemberDefinition $s -Name User32 -Namespace GetKeyState -PassThru
return [bool]($t::GetKeyState($k) -band 0x80)
}
## Startup Framework
################################################################
Add-Type -Assembly PresentationFramework
Add-Type -Assembly PresentationCore
$app = [Windows.Application]::New()
[xml]$XAML = Get-Content ($DIR_APP + "\Z4.xaml")
$Read = (New-Object System.Xml.XmlNodeReader $XAML);
try{ $Script:core = [Windows.Markup.XamlReader]::Load($Read) }catch{ write-host "[ERR]::XAML >> CORE_MAIN" -f "red"; exit; }
$XAML.SelectNodes("//*[#Name]") | %{ Set-Variable -Name ($_.Name) -Value $Script:core.FindName($_.Name) -Scope Script; }
$core.Add_Activated({ Initialization })
## Framework
################################################################
function Initialization()
{
# Button = 2 + INCOMPLETE
$coreAppName.Text = "Core v" + $ver;
$coreClose.Add_Click({ $core.Close(); });
$coreTest.Add_Click({ ClickTest });
$core.show();
}
## Application
################################################################
## GUI
################################################################
function ClickTest()
{
$coreTest.Style = $core.tryFindResource("BlueMe");
}
$app.Run($core);
XAML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title=""
MinWidth="1"
MinHeight="1"
UseLayoutRounding="True"
SnapsToDevicePixels="True"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
Topmost="True"
Background="#111"
WindowStartupLocation="CenterScreen"
WindowStyle="None">
<Grid Background="#111" Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#FFF"/>
<Setter Property="Margin" Value="4, 4, 0, 0"/>
</Style>
<Style x:Key="CloseRed" TargetType="{x:Type Button}">
<Setter Property="Background" Value="#111"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Height" Value="23"/>
<Setter Property="Width" Value="23"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="White" BorderThickness="0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#300"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="#FFF"/>
<Setter Property="Background" Value="#222"/>
<Setter Property="Margin" Value="4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="White" BorderThickness="0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#333"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="BlueMe" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="#FFF"/>
<Setter Property="Background" Value="#138"/>
<Setter Property="Margin" Value="4"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="White" BorderThickness="0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#14A"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="#222"/>
<Setter Property="BorderBrush" Value="#333"/>
<Setter Property="Foreground" Value="#FFF"/>
<Setter Property="Margin" Value="4, 0, 4, 0"/>
</Style>
</Grid.Resources>
<TextBlock Grid.Row="0" Grid.Column="0" Name="coreAppName" Text="Core" Margin="4,2" FontSize="12"/>
<Button Grid.Row="0" Grid.Column="1" Name="coreClose" Style="{StaticResource CloseRed}" HorizontalAlignment="Right" VerticalAlignment="Top">
<Path Margin="-8,-8,0,0" Fill="#F00" Data="M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z">
<Path.LayoutTransform>
<ScaleTransform ScaleX="0.4" ScaleY="0.4"/>
</Path.LayoutTransform>
</Path>
</Button>
<Button Grid.Row="1" Grid.ColumnSpan="2" Name="coreTest" Content="Test" Width="96" Height="48"/>
</Grid>
</Window>```

I figured it out after a bunch of trial and error (and reading the MS XAML KB).
The Style and Key must be in Window.Resources for $core.tryFindResource("Key") to find it.

Related

Changing Border of WPF Tab Control Dynamically

I'm using my own control template for the TabControl and TabItem in the application resource of my project.
In the template, the selected TabItem is colored depending on a property 'SelectedBrush' which returns a Brush. I would also like the Border of the Tab Control (boTabControl, the border around the content presenter) to do be the same color as the selected TabItem.
Is this a code thing, or can it be done in the application resource?
<Style TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabPanel Grid.Row="0" Panel.ZIndex="1" Margin="0,3,0,0" IsItemsHost="True" Background="Transparent"/>
<Border x:Name="boTabControl" Grid.Row="1" BorderThickness="1,3,0,0" CornerRadius="0" Padding="0" Margin="0" BorderBrush="{DynamicResource SelectedBrush}">
<ContentPresenter ContentSource="SelectedContent" Margin="0"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter TargetName="boTabControl" Property="BorderBrush" Value="{Binding SelectedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="boTabControl" Property="BorderBrush" Value="Blue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Panel" Background="Transparent">
<Border x:Name="Border" Margin="0,0,-3,0" BorderThickness="1,1,1,0" CornerRadius="10,30,0,0">
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ContentSource="Header" Margin="10"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="{Binding SelectedBrush}" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="Background" Value="LightGray" />
<Setter Property="FontWeight" Value="Normal" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Public ReadOnly Property SelectedBrush As Brush
Get
If Me.tcMain.SelectedItem Is Nothing Then
Return Brushes.Aquamarine
Exit Property
End If
Dim myTabItem As TabItem = Me.tcMain.SelectedItem
Dim myBrush As Brush = Brushes.Blue
Select Case myTabItem.Name
Case Me.tiSale.Name
myBrush = Brushes.Green
Case Me.tiReturn.Name
myBrush = Brushes.Red
Case Me.tiStock.Name
myBrush = Brushes.Black
Case Me.tiAdmin.Name
myBrush = Brushes.Purple
End Select
Return myBrush
End Get
End Property
here is an approach with an attached property SelectedBrush of type Brush
add Attached class to project (and make build):
public class Attached
{
public static DependencyProperty SelectedBrushProperty = DependencyProperty.RegisterAttached("SelectedBrush", typeof(Brush), typeof(Attached));
public static Brush GetSelectedBrush(DependencyObject obj)
{
return (Brush) obj.GetValue(SelectedBrushProperty);
}
public static void SetSelectedBrush(DependencyObject obj, Brush value)
{
obj.SetValue(SelectedBrushProperty, value);
}
}
then assign colors to each TabItem:
<TabControl>
<TabItem Name="tiSale" local:Attached.SelectedBrush="Green" Header="123"/>
<TabItem Name="tiReturn" local:Attached.SelectedBrush="Red" Header="abc"/>
<TabItem Name="tiStock" local:Attached.SelectedBrush="Black" Header="XYZ"/>
</TabControl>
change TabControl template to use Attached.SelectedBrush:
<Style TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabPanel Grid.Row="0" Panel.ZIndex="1" Margin="0,3,0,0" IsItemsHost="True" Background="Transparent"/>
<Border x:Name="boTabControl" Grid.Row="1"
BorderThickness="1,3,0,0"
BorderBrush="{Binding Path=SelectedItem.(local:Attached.SelectedBrush), RelativeSource={RelativeSource TemplatedParent}}"
CornerRadius="0" Padding="0" Margin="0">
<ContentPresenter ContentSource="SelectedContent" Margin="0"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
to use the same brush for TabItem, change Background setter like this:
<Setter TargetName="Border" Property="Background"
Value="{Binding (local:Attached.SelectedBrush), RelativeSource={RelativeSource TemplatedParent}}" />
SelectedBrush property from code behind is not used here

WPF Control Template not being applied to first item rendered

I'm building a WPF application and I have a control template defined in App.XAML for a combobox.
When I start the application, the first combo box that is rendered does not use the style defined in the control template while all other do. Also, the first combo box that I open has an unstyled first item while the rest of the items in that combo box and others are styled.
What am I missing?
Here's the app.xaml:
<Application x:Class="WpfPropertyGrid_Demo.App"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<!--Brushes defined here-->
</ResourceDictionary>
<ResourceDictionary>
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border
Grid.ColumnSpan="2"
Background="{StaticResource WindowBackgroundBrush}" />
<Border
x:Name="Border"
Grid.Column="1"
Background="{StaticResource WindowBackgroundBrush}" />
<Path
x:Name="Arrow"
Grid.Column="1"
Fill="{StaticResource GlyphBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource ActiveBrush}" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource ActiveBrush}" />
<Setter TargetName="Arrow" Property="Fill" Value="{StaticResource ActiveGlyphBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
<Setter TargetName="Arrow" Property="Fill" Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
</ControlTemplate>
<Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,3,23,3"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid
Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border
x:Name="DropDownBorder"
Background="{StaticResource ActiveBrush}" />
<ScrollViewer Margin="4,4,4,4" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="IsEditable"
Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
<!-- SimpleStyles: ComboBoxItem -->
<Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border Name="Border" SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Foreground" Value="{StaticResource ActiveGlyphBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Here's the actual window xaml:
<Window x:Class="WpfPropertyGrid_Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:wpg="clr-namespace:System.Windows.Controls"
Title="WpfPropertyGrid Demo" mc:Ignorable="d" ResizeMode="CanResizeWithGrip"
Width="360" Height="360" MinWidth="360" MinHeight="400">
<Window.Resources>
<ObjectDataProvider x:Key="SortTypes" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="wpg:PropertySort"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical" Margin="0,20,12,0" HorizontalAlignment="Right" VerticalAlignment="Top">
<ComboBox Name="ComboSort" Margin="0,3,0,0" Width="95" FontSize="10"
SelectedIndex="0" ItemsSource="{Binding Source={StaticResource SortTypes}}" />
<ComboBox Name="ComboSort2" Margin="0,3,0,0" Width="95" FontSize="10"
SelectedIndex="0" ItemsSource="{Binding Source={StaticResource SortTypes}}" />
</StackPanel>
</Grid>
Please put your ComboBox style in a separate ResourceDictionary, let say ComboBox.xaml and merge this ComboBox.xaml to the App.xaml.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ComboBox.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
I would suggest to create a separate ResourceDictionary for every control you create your own Template. This way it would be easy to manage your resources.

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

How can I access to an object in a control of wpf, programmatically?

<TabControl x:Class="MyTabControl.Tab_Control"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyTabControl" Padding="0" Background="White" BorderBrush="Black" Loaded="TabControl_Loaded">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Name="Grid_Main">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="1" Name="border_Main" Background="#F0F0F0" BorderBrush="LightGray" BorderThickness="1,1,1,0" CornerRadius="4,4,0,0"
Margin="-2,0,2,0" SnapsToDevicePixels="True" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="13" HorizontalAlignment="Center" Name="TextBlock"
Foreground="DarkGray">
<ContentPresenter x:Name="ContentSite" VerticalAlignment="Center"
HorizontalAlignment="Center" ContentSource="Header" Margin="5,2,5,2"/></TextBlock>
</StackPanel>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="border_Main" Property="Background" Value="White" />
<Setter TargetName="border_Main" Property="BorderBrush" Value="Gray" />
<Setter TargetName="TextBlock" Property="Foreground" Value="Black" />
<Setter TargetName="border_Main" Property="Margin" Value="-2,0,2,-1" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True" SourceName="border_Main" >
<Setter TargetName="border_Main" Property="Background" Value="White" />
<Setter TargetName="border_Main" Property="BorderBrush" Value="DarkGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
</TabControl>
How can I access my "border_Main" control programmatically?
I would recommend using LinqToVisualTree ...
http://www.scottlogic.co.uk/blog/colin/2010/03/linq-to-visual-tree/
You can find all the "border_Main" controls within the TabControl as follows:
tabControl.Descendants<Border>().Where(d => d.Name=="border_Main");
Or, if you could use the above query on a TabItem directly if you want to find a single border_Main instance.

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