Create WPF context menu for a TreeView using HierarchicalDataTemplates - wpf

The following code produces a TreeView as seen below. When you right click any of the child nodes (not parents), I would like a simple context menu to display.
Here is the code I am using to create the tree view. I need to use the HierarchicalDataTemplate so the solution must include that.
XAML
<Window x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
mc:Ignorable="d">
<Grid>
<TreeView ItemsSource="{Binding Parents}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Parent}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Child}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>
CODE
using System.Collections.Generic;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel
{
public ViewModel()
{
Parents = new List<Parent>();
Parents.Add(new Parent()
{
Name = "Parent A",
Children = new List<Child>() {
new Child() { Name = "Child A" },
new Child() { Name = "Child B" }
}
});
Parents.Add(new Parent()
{
Name = "Parent B",
Children = new List<Child>() {
new Child() { Name = "Child C" },
new Child() { Name = "Child D" }
}
});
}
public List<Parent> Parents { get; set; }
}
public class Parent
{
public Parent() { Children = new List<Child>(); }
public string Name { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public string Name { get; set; }
}
}
SAMPLE CONTEXT MENU
<ContextMenu x:Key ="ArchiveFaxNodePopupMenu">
<MenuItem Header="Delete" />
</ContextMenu>
Thanks for the help!
UPDATE
Here is the updated XAML that makes the content menu work for the child node types only (thanks to #EdPlunket for the answer)
<Window x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
mc:Ignorable="d">
<Grid>
<TreeView ItemsSource="{Binding Parents}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Parent}"
ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Child}">
<TextBlock Text="{Binding Name}">
<TextBlock.Resources>
<ContextMenu x:Key ="ArchiveFaxNodePopupMenu">
<MenuItem Header="Delete" />
</ContextMenu>
</TextBlock.Resources>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="ContextMenu" Value="{StaticResource ArchiveFaxNodePopupMenu}" />
</Style>
</TextBlock.Style>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>

This should do it.
<TreeView ItemsSource="{Binding Parents}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="ContextMenu" Value="{StaticResource ArchiveFaxNodePopupMenu}" />
</Style>
</TreeView.ItemContainerStyle>
<!-- resources etc. -->
If you want different context menus for the different types of items, put them on the TextBlocks in the templates.

Related

Why are hotkeys not working when using DataTemplate for dynamic menu?

I'm able to bind a collection of my own type to a menu. But I'm struggling with the hotkeys. Either the hotkeys show up but don't work or the hotkeys don't show up but work.
Do you have any idea what goes wrong here?
Here is the stripped-down code:
public class Category : DependencyObject
{
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.Register("Caption", typeof(string), typeof(Category), new PropertyMetadata(null));
public ObservableCollection<Item> SubItems
{
get { return (ObservableCollection<Item>)GetValue(SubItemsProperty); }
set { SetValue(SubItemsProperty, value); }
}
public static readonly DependencyProperty SubItemsProperty =
DependencyProperty.Register("SubItems", typeof(ObservableCollection<Item>), typeof(Category), new PropertyMetadata(null));
}
public class Item : DependencyObject
{
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(Item), new PropertyMetadata(null));
}
public class MenuViewModel : DependencyObject
{
public ObservableCollection<Category> MenuCategories
{
get { return (ObservableCollection<Category>)GetValue(MenuCategoriesProperty); }
set { SetValue(MenuCategoriesProperty, value); }
}
public static readonly DependencyProperty MenuCategoriesProperty =
DependencyProperty.Register("MenuCategories", typeof(ObservableCollection<Category>), typeof(MenuViewModel), new PropertyMetadata(null));
public MenuViewModel()
{
MenuCategories = new ObservableCollection<Category>()
{
new Category() {Caption = "_One", SubItems = new ObservableCollection<Item>() { new Item() { Header = "_one"}, new Item() { Header = "t_wo" }, new Item() { Header = "_three" } } },
new Category() {Caption = "_Two", SubItems = new ObservableCollection<Item>() { new Item() { Header = "_one"}, new Item() { Header = "t_wo" }, new Item() { Header = "_three" } } },
};
}
}
public partial class Test1Window : Window
{
public Test1Window()
{
InitializeComponent();
DataContext = new MenuViewModel();
}
}
public class MenuItemContainerTemplateSelector : ItemContainerTemplateSelector
{
public override DataTemplate SelectTemplate(object item, ItemsControl parentItemsControl)
{
var key = new DataTemplateKey(item.GetType());
return (DataTemplate)parentItemsControl.FindResource(key);
}
}
And the xaml 1. try:
<Window x:Class="DynamicMenuTest.Test1Window"
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:local="clr-namespace:DynamicMenuTest"
mc:Ignorable="d"
Title="Test1Window" Height="300" Width="300">
<Window.Resources>
<local:MenuItemContainerTemplateSelector x:Key="MenuItemContainerTemplateSelector"/>
<DataTemplate DataType="{x:Type local:Item}">
<MenuItem Header="{Binding Header}">
<MenuItem.HeaderTemplate>
<DataTemplate>
<AccessText VerticalAlignment="Center" Text="{Binding}" />
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Category}" ItemsSource="{Binding SubItems}">
<MenuItem VerticalContentAlignment="Center">
<!-- Hotkey works but doesn't show up -->
<MenuItem.Header>
<AccessText Text="{Binding Caption}"/>
</MenuItem.Header>
</MenuItem>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Menu ItemsSource="{Binding MenuCategories}"
UsesItemContainerTemplate ="true"
ItemContainerTemplateSelector="{StaticResource MenuItemContainerTemplateSelector}"
/>
</Grid>
Xaml 2. try:
<Window x:Class="DynamicMenuTest.Test2Window"
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:local="clr-namespace:DynamicMenuTest"
mc:Ignorable="d"
Title="Test2Window" Height="300" Width="300">
<Window.Resources>
<local:MenuItemContainerTemplateSelector x:Key="MenuItemContainerTemplateSelector"/>
<DataTemplate DataType="{x:Type local:Item}">
<MenuItem Header="{Binding Header}">
<MenuItem.HeaderTemplate>
<DataTemplate>
<AccessText VerticalAlignment="Center" Text="{Binding}" />
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Category}" ItemsSource="{Binding SubItems}">
<MenuItem
Header="{Binding Caption}"
VerticalContentAlignment="Center">
<!-- Hotkey shows up but menu not working -->
<MenuItem.HeaderTemplate>
<DataTemplate>
<ContentPresenter RecognizesAccessKey="True" Content="{Binding }" VerticalAlignment="Center" Margin="4,0"/>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Menu ItemsSource="{Binding MenuCategories}"
UsesItemContainerTemplate ="true"
ItemContainerTemplateSelector="{StaticResource MenuItemContainerTemplateSelector}"
/>
</Grid>
Xaml 3. try:
<Window x:Class="DynamicMenuTest.Test3Window"
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:local="clr-namespace:DynamicMenuTest"
mc:Ignorable="d"
Title="Test3Window" Height="300" Width="300">
<Window.Resources>
<local:MenuItemContainerTemplateSelector x:Key="MenuItemContainerTemplateSelector"/>
<DataTemplate DataType="{x:Type local:Item}">
<MenuItem Header="{Binding Header}">
<MenuItem.HeaderTemplate>
<DataTemplate>
<AccessText VerticalAlignment="Center" Text="{Binding}" />
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Category}" ItemsSource="{Binding SubItems}">
<MenuItem
Header="{Binding Caption}"
VerticalContentAlignment="Center">
<!-- Hotkey shows up but menu doesn't work -->
<MenuItem.HeaderTemplate>
<DataTemplate>
<AccessText Text="{Binding}" VerticalAlignment="Center" Margin="4,0"/>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Menu ItemsSource="{Binding MenuCategories}"
UsesItemContainerTemplate ="true"
ItemContainerTemplateSelector="{StaticResource MenuItemContainerTemplateSelector}"
/>
</Grid>
Your DataTemplate and HierarchicalDataTemplate describe how the header content of your MenuItem should look like, so nesting another MenuItem there is a bad idea. Instead, keep it really simple and enjoy:
<DataTemplate DataType="{x:Type local:Item}">
<AccessText VerticalAlignment="Center" Text="{Binding Header}" />
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Category}" ItemsSource="{Binding SubItems}">
<AccessText Text="{Binding Caption}"/>
</HierarchicalDataTemplate>

How to make a recursive ListView in XAML

My model looks like
public class MyVm
{
public string MyTitle { get; set; }
public List<MyVm> Children { get; set; }
public MyVm()
{
this.Children = new List<MyVm>();
}
}
I want to be able to list through all the children and children's children, which I think is recursive.
The MainWindow code behind is
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Kids = new List<MyVm>();
var m = new MyVm();
m.MyTitle = "Title1";
var m2 = new MyVm();
m2.MyTitle = "Title2";
var m3 = new MyVm();
m3.MyTitle = "Title3";
var m4 = new MyVm();
m4.MyTitle = "Title4";
m.Children.Add(m2);
m2.Children.Add(m3);
m3.Children.Add(m4);
this.Kids.Add(m);
}
public List<MyVm> Kids { get; set; }
and finally the MainWIndow view is
<Grid.Resources>
<Style x:Key="MyStyle" TargetType="ListViewItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<HierarchicalDataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MyTitle}" />
<ListView ItemsSource="{Binding Children}" ItemContainerStyle="{Binding MyStyle}" />
</StackPanel>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ListView ItemsSource="{Binding Kids}" ItemContainerStyle="{StaticResource MyStyle}" />
As you can see I've tried to re-use the same resource for each 'children' to achieve the recursive bit, but sadly, the only thing I see rendered is a single TextBlock with Title2
For these purposes you would use a HierarchicalDataTemplate (which has its own ItemsSource property that you would bind to Children), i am not sure if a ListBox supports it. If not use a TreeView and change the control templates to remove the indentation and the collapse toggle button if you don't want that.
Fixed it
<Grid.Resources>
<DataTemplate DataType="{x:Type a:MyVm}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MyTitle}" />
<ListView ItemsSource="{Binding Children}">
<ListView.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding }" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</Grid.Resources>
<ListView ItemsSource="{Binding Kids}" />

WPF: Preventing the ContextMenu from appearing on child TreeViewItems

I am trying to make design a WPF TreeView where the ContextMenu activates on particular nodes.
In my example, despite my best efforts, I cannot keep the ContextMenu of a BarNode from appearing when it's children 'FooNode's are clicked.
C#:
public abstract class NodeBase
{
public NodeBase[] ChildNodes { get; set; }
}
public class FooNode : NodeBase
{
}
public class BarNode : NodeBase
{
}
public class ExampleModel : BaseModel
{
private NodeBase[] _nodes;
public NodeBase[] Nodes
{
get
{
_nodes = new NodeBase[]
{
new FooNode(),
new BarNode()
{
ChildNodes = new NodeBase[]
{
new FooNode(),
new FooNode()
}
}
};
return _nodes;
}
}
public ExampleModel()
{
}
}
public class TreeViewStyleSelector : StyleSelector
{
public Style FooNodeStyle { get; set; }
public Style BarNodeStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
var fooNode = item as FooNode;
if (fooNode != null)
{
return FooNodeStyle;
}
var barNode = item as BarNode;
if (barNode != null)
{
return BarNodeStyle;
}
return base.SelectStyle(item, container);
}
}
XAML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Nodes="clr-namespace:UnderstandingWPFTreeView.Nodes"
xmlns:Models="clr-namespace:UnderstandingWPFTreeView.Models"
xmlns:Common="clr-namespace:UnderstandingWPFTreeView.Common"
x:Class="UnderstandingWPFTreeView.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<Models:ExampleModel/>
</Window.DataContext>
<Window.Resources>
<ContextMenu x:Key="testContextMenu">
<MenuItem Header="Test Context Item"></MenuItem>
<MenuItem Header="Test Context Item"></MenuItem>
</ContextMenu>
<Style TargetType="{x:Type TreeViewItem}" x:Key="FooNodeStyle">
</Style>
<Style TargetType="{x:Type TreeViewItem}" x:Key="BarNodeStyle">
<Setter Property="ContextMenu" Value="{StaticResource testContextMenu}" />
</Style>
<Common:TreeViewStyleSelector
x:Key="treeViewStyleSelector"
FooNodeStyle="{StaticResource ResourceKey=FooNodeStyle}"
BarNodeStyle="{StaticResource ResourceKey=BarNodeStyle}" />
</Window.Resources>
<StackPanel HorizontalAlignment="Left" Height="320" VerticalAlignment="Top" Width="517">
<TreeView Height="100">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type Nodes:BarNode}" ItemsSource="{Binding Path=ChildNodes}">
<TextBlock Text="Bar" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type Nodes:FooNode}" ItemsSource="{Binding Path=ChildNodes}">
<TextBlock Text="Foo" />
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeViewItem Header="Testing" ItemsSource="{Binding Nodes}" ItemContainerStyleSelector="{StaticResource ResourceKey=treeViewStyleSelector}"/>
</TreeView>
</StackPanel>
</Window>
I asked the same question of the MSDN Forums and got an answer that I can confirm as working.
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7dd183bc-d616-4ec4-8b2a-0b438c9a115c
Placing the ContextMenu objects on the TextBlock gives the same visual appearance, minus the effect of passing the ContextMenu down the chain of TreeNodes.
<HierarchicalDataTemplate DataType="{x:Type local:BarNode}" ItemsSource="{Binding Path=ChildNodes}">
<TextBlock Text="Bar" ContextMenu="{StaticResource testContextMenu}" />
</HierarchicalDataTemplate>

When setting contextmenu via style setter, PlacementTarget property is null

In my application, I have a view (ListView) and a view model. Inside view model, I have 2 properties: first is a list of items, and the second is a command. I want to display items (from first property) inside ListView. In addition, I want to have for each one a context menu, where clicking on it will activate a command (from second property).
Here is a code of my view model:
public class ViewModel
{
public IEnumerable Items
{
get
{
return ...; //returns a collection of items
}
}
public ICommand MyCommand //this is a command, I want to be able execute from context menu of each item
{
get
{
return new DelegateCommand(new Action<object>(delegate(object parameter)
{
//here code of the execution
}
), new Predicate<object>(delegate(object parameter)
{
//here code of "can execute"
}));
}
}
Now the XAML part:
<ListView ItemsSource="{Binding Items}">
<ListView.Resources>
<commanding:CommandReference x:Key="myCommand" Command="{Binding MyCommand}"/>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Name}"
/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem
Header="Remove from workspace"
Command="{StaticResource myCommand}"
CommandParameter="HERE I WANT TO PASS THE DATA CONTEXT OF THE ListViewItem"
/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
The problem: until I actually opening context menu, the PlacementTarget of the context menu is null. I need somehow to receive data context of the clicked ListViewItem into "CanExecute" of the command, BEFORE the command being called - and I truly wish to make everything in the XAML, without handling any callbacks in code behind.
Thank you in advance.
If you are looking for ListViewItem's DataContext you can do this:
CommandParameter="{Binding}"
Edit - Here is what I tried:
public partial class MainWindow : Window
{
private ObservableCollection<Person> list = new ObservableCollection<Person>();
public MainWindow()
{
InitializeComponent();
list.Add(new Person() { Name = "Test 1"});
list.Add(new Person() { Name = "Test 2"});
list.Add(new Person() { Name = "Test £"});
list.Add(new Person() { Name = "Test 4"});
this.DataContext = this;
}
public static ICommand MyCommand //this is a command, I want to be able execute from context menu of each item
{
get
{
return new DelegateCommand<Person>(
a => Console.WriteLine(a.Name),
a => true);
}
}
public ObservableCollection<Person> Items
{
get
{
return this.list;
}
}
}
public class Person
{
public string Name { get; set; }
}
And the xaml:
<Window x:Class="ListView1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ListView1="clr-namespace:ListView1" Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Remove from workspace" Command="{x:Static ListView1:MainWindow.MyCommand}" CommandParameter="{Binding}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
</Window>

WPF: Setting a binding for all TreeViewItem instance

Greetings,
I'm using WPF with a Model-View-ViewModel pattern, and I have a view model with an IsSelected property which I want to bind to a TreeViewItem's IsSelected property for all TreeViewItems in the scope. I'm attempting to do this with a Style and a Setter. This works apparently for the root-level TreeViewItems, but not for their children. Why is this? How can I have this apply to all TreeViewItem controls?
Here is the view XAML:
<UserControl x:Class="MyApp.AllAreasView"
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:MyApp="clr-namespace:MyApp"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="700">
<UserControl.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
<MyApp:CountVisibilityConverter x:Key="CountVisibilityConverter" />
<HierarchicalDataTemplate x:Key="AreaTemplate"
DataType="AreaViewModel"
ItemsSource="{Binding Path=SubareasCollectionView}">
<WrapPanel>
<TextBlock Text="{Binding Path=Name}" Margin="0 0 8 0" />
<TextBlock DataContext="{Binding Path=Subareas}"
Text="{Binding Path=Count, StringFormat= (\{0\})}"
Visibility="{Binding Path=Count, Converter={StaticResource CountVisibilityConverter}}" />
</WrapPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<TreeView ItemsSource="{Binding TopLevelAreas}"
ItemTemplate="{StaticResource AreaTemplate}">
</TreeView>
</UserControl>
I think we'll need more info to answer your question. Specifically, what your view model(s) look like. Below is an example you can copy and paste that works fine.
Window1.xaml:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Background" Value="{Binding Background}"/>
</Style>
<HierarchicalDataTemplate x:Key="ItemTemplate" DataType="local:DataItem" ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource ItemTemplate}"/>
</Window>
Window1.xaml.cs:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Media;
namespace WpfApplication1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var dis = new ObservableCollection<DataItem>();
var di = new DataItem() { Name = "Top", Background = Brushes.Red };
di.Children.Add(new DataItem() { Name = "Second", Background = Brushes.Blue });
dis.Add(di);
DataContext = dis;
}
}
public class DataItem
{
public DataItem()
{
Children = new ObservableCollection<DataItem>();
}
public string Name
{
get;
set;
}
public ICollection<DataItem> Children
{
get;
set;
}
public Brush Background
{
get;
set;
}
}
}
working with view models you will get very friendly with the ItemContainerStyle property. what you were doing in your code set the data template for the root level. what you want to do is style each of the items in the treeview, which you can do like so.
<TreeView ItemsSource="{Binding TopLevelAreas}"
ItemContainerStyle="{StaticResource AreaTemplate}">
</TreeView>
enjoy
You have to use as mentioned below. Make use of BasedOn option
<TreeView ItemsSource="{Binding TopLevelAreas}">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource StyleKey}"/>
</TreeView.Resources>
</TreeView>

Resources