How can I add additional item templates to an extended WPF treeview - wpf

I'm trying to set up a Treeview descendent class that can be used as a common template for
all Treeview instances in my application, but with additional formatting and templates for each instance.
For the base, I have a UserControl that descends from Treeview, with the common styles and a single standard data template
<TreeView x:Class="BaseTreeView" ... >
<TreeView.ItemContainerStyle> ... </TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type local:BaseTreeViewItem}">
<TextBlock Text="{Binding Caption}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Then in each window, I use this extended Treeview and add additional data templates for the specific TreeviewItems I'm displaying.
e.g.
<Window x:Class="Window1" ... >
...
<BaseTreeView ItemsSource="{Binding RootTreeItems}" >
<MyTreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type ExtendedTreeViewItem1}">
<StackPanel Orientation="Horizontal">
<Image Source="Images/Image1.png" />
<TextBlock Text="{Binding Caption}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ExtendedTreeViewItem2}">
<StackPanel Orientation="Horizontal">
<Image Source="Images/Image2.png" />
<TextBlock Text="{Binding Caption}" />
</StackPanel>
</DataTemplate>
</MyTreeView.Resources>
</BaseTreeView>
...
</Window>
This compiles fine, but at runtime I get an error
"'Set property 'System.Windows.ResourceDictionary.DeferrableContent' threw an exception.' Line number '27' and line position '59'."
"Cannot re-initialize ResourceDictionary instance."
Is there any way around this, or can someone suggest a better way to set up a base treeview template and multiple descedent versions.

You could try moving your templates to the <Window.Resources> instead of <MyTreeView.Resources>
If it doesn't work, maybe using a DataTemplateSelector suits your case best. You can create a DataTemplateSelector class like this:
public class ExtendedTreeViewTemplateSelector : DataTemplateSelector
{
public DataTemplate ExtendedTreeViewItem1Template { get; set; }
public DataTemplate ExtendedTreeViewItem2Template { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is ExtendedTreeViewItem1)
return ExtendedTreeViewItem1Template;
if (item is ExtendedTreeViewItem2)
return ExtendedTreeViewItem2Template;
}
}
And then use it in your XAML like this:
<Window x:Class="Window1" ... >
<Window.Resources>
<HierarchicalDataTemplate x:Key="extendedTreeViewItem1Template" ItemsSource="{Binding Children}" DataType="{x:Type ExtendedTreeViewItem1}">
<StackPanel Orientation="Horizontal">
<Image Source="Images/Image1.png" />
<TextBlock Text="{Binding Caption}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate x:Key="extendedTreeViewItem2Template" DataType="{x:Type ExtendedTreeViewItem2}">
<StackPanel Orientation="Horizontal">
<Image Source="Images/Image2.png" />
<TextBlock Text="{Binding Caption}" />
</StackPanel>
</DataTemplate>
<selector:ExtendedTreeViewTemplateSelector x:Key="treeViewTemplateSelector"
ExtendedTreeViewItem1Template="{StaticResource extendedTreeViewItem1Template}"
ExtendedTreeViewItem2Template="{StaticResource extendedTreeViewItem2Template}" />
</Window.Resources>
...
<BaseTreeView ItemsSource="{Binding RootTreeItems}"
ItemTemplateSelector={StaticResource treeViewTemplateSelector}" />
...
</Window>

Related

Data binding in TreeViewItem (HierachicalDataTemplate)

I have a tree view nicely connected to my model with a MVVM pattern. I'm running into an issue with the context menu. I do not know how to bind to an element outside the tree. I have created a sample that demonstrates what I'm trying. I hope someone explain to me what I'm doing wrong.
In the sample I have five TextBlocks in the header, each binding differently to the data. And I have a context menu with five entries, again binding differently. The commentary says which binding works and which doesn't. There's even a difference between the header and the context menu.
Some bindings try to bind to another element in the UI another to a property in the view model.
Here's the XAML:
<Window x:Class="WpfApp7_TreeView.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:WpfApp7_TreeView"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
x:Name="root">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding SomeProperty}" /> <!-- ok -->
<TextBlock Grid.Row="1" Text="Text from XAML" x:Name="tb" /> <!-- ok -->
<TreeView Grid.Row="2" HorizontalAlignment="Stretch" ItemsSource="{Binding Departments}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Department}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5" Text="{Binding DepartmentName }" /> <!-- ok -->
<TextBlock Margin="5" Text="{Binding ElementName=tb, Path=Text}"/> <!-- ok -->
<TextBlock Margin="5" Text="{Binding SomeProperty}"/> <!-- no show -->
<TextBlock Margin="5" Text="{Binding ElementName=root, Path=DataContext.SomeProperty}"/> <!-- ok -->
<TextBlock Margin="5" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},
Path=DataContext.SomeProperty}"/> <!-- ok -->
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Some command" /> <!-- ok -->
<MenuItem Header="{Binding ElementName=tb, Path=Text}" /> <!-- no show -->
<MenuItem Header="{Binding SomeProperty}" /> <!-- no show -->
<MenuItem Header="{Binding ElementName=root, Path=DataContext.SomeProperty}"/> <!-- no show -->
<MenuItem Header="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},
Path=DataContext.SomeProperty}" /> <!-- no show -->
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
And here's the view model:
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace WpfApp7_TreeView
{
public class MainWindowViewModel : ViewModelBase
{
private string someProperty = "Text from the viewmodel";
public string SomeProperty
{
get { return someProperty; }
set { someProperty = value; OnPropertyChanged("SomeProperty"); }
}
public MainWindowViewModel()
{
Departments = new List<Department>()
{
new Department("Department 1"),
new Department("Department 2")
};
}
private List<Department> departments;
public List<Department> Departments
{
get {return departments; }
set { departments = value; OnPropertyChanged("Departments"); }
}
}
public class Department : ViewModelBase
{
public Department(string depname)
{
DepartmentName = depname;
}
public string DepartmentName { get; set; }
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propname));
}
}
}
}
TreeViewItem DataContexts get set to their list item, so the SomeProperty binding in the parent HierarchicalDataTemplate needs to use a RelativeSource binding instead. Change this:
<TextBlock Margin="5" Text="{Binding SomeProperty}"/> <!-- no show -->
... to this:
<TextBlock Margin="5" Text="{Binding DataContext.SomeProperty, RelativeSource={RelativeSource AncestorType=TreeView}}"/>
With respect to your ContextMenu, you are correct in noting that bindings have to be part of the visual tree. The solution to this is to bind via an intermediate Binding Proxy instead. Generally speaking you would bind to the parents DataContext, rather than directly to another control, but it can be done both ways:
<TreeView Grid.Row="2" HorizontalAlignment="Stretch" ItemsSource="{Binding Departments}">
<TreeView.Resources>
<local:BindingProxy x:Key="TextBlockProxy" Data="{Binding Text, ElementName=tb}" />
<local:BindingProxy x:Key="MainViewModelProxy" Data="{Binding}" />
</TreeView.Resources>
<TreeView.ItemTemplate>
...etc...
And then in the ContextMenu:
<!-- Binds to the TextBlock's Text property -->
<MenuItem Header="{Binding Data, Source={StaticResource TextBlockProxy}}" />
<!-- Binds to the main view model's SomeProperty -->
<MenuItem Header="{Binding Data.SomeProperty, Source={StaticResource MainViewModelProxy}}" />

Is it possible to have a DataTemplate around another DataTemplate?

Hello everyone and welcome to yet another nested DataTemplate question!
In this one, I'd like to have a DataTemplate like this, written on a ResourceDictionary:
<DataTemplate x:Key="Vector3Template">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<xctk:DoubleUpDown Tag="X" Style="{StaticResource DoubleUpDownStyle}" Value="{Binding X}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<xctk:DoubleUpDown Tag="Y" Style="{StaticResource DoubleUpDownStyle}" Value="{Binding Y}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<xctk:DoubleUpDown Tag="Z" Style="{StaticResource DoubleUpDownStyle}" Value="{Binding Z}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
Being surrounded by a DataTemplate with a border, like the following, also written on a ResourceDictionary (in the future it's gonna have a couple more elements to it):
<DataTemplate x:Key="ComponentTemplate">
<Border Margin="5" BorderThickness="2" BorderBrush="Gray"/>
</DataTemplate>
Why would I want this, you ask? Well, I'm trying to display an ObservableCollection of IComponent named _components and I want all instances to share the same Borders, but with its core being specific to every class type that inherits from IComponent.
In order to display the list with its differents type, I'm using the following code on a UserControl:
<Grid x:Name="LayoutRoot" Background="White">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel>
<ListView x:Name="_componentsList"
ItemsSource="{Binding Components}"
HorizontalContentAlignment="Stretch">
<ListView.Resources>
<DataTemplate DataType="{x:Type models:Transform}">
<ContentControl Content="{StaticResource ComponentTemplate}" ContentTemplate="{StaticResource TransformTemplate}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type models:Vector3}">
<ContentPresenter ContentTemplate="{StaticResource Vector3Template}"/>
</DataTemplate>
</ListView.Resources>
</ListView>
</StackPanel>
</ScrollViewer>
Trying to build this system with Prism 6.3 and with almost no code-behind, every c# code that I have is just for models, so no real logic here so far.
Is this possible? How so? I've started playing with WPF a few days ago and still have a lot to learn.
I believe what you're looking for is to simply use a DataTemplateSelector in which the DataTemplate used is determined by the data. You can find a full tutorial here. Once you've setup your DataTemplateSelector you simply would pass it in as the DataTemplate to your control.
public class PropertyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultnDataTemplate { get; set; }
public DataTemplate BooleanDataTemplate { get; set; }
public DataTemplate EnumDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
DependencyPropertyInfo dpi = item as DependencyPropertyInfo;
if (dpi.PropertyType == typeof(bool))
{
return BooleanDataTemplate;
}
if (dpi.PropertyType.IsEnum)
{
return EnumDataTemplate;
}
return DefaultnDataTemplate;
}
}

WPF Sorting ItemsControl under DataTemplate

I am using ItemsControl under DataTemplate. I want to sort ItemsControl ic using id column.
<DataTemplate x:Key="With">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock Text="{Binding Path=fil}" Style="{StaticResource Fixed}" Margin="0,0,6,8" />
<mui:ModernButton IconData="{StaticResource PlayIconData}" Click="FullPlayback" Margin="0,0,6,8" ></mui:ModernButton>
</StackPanel>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Text="{Binding Path=e1}" Foreground="Red" Style="{StaticResource Fixed}" Margin="0,0,6,8" />
<TextBlock Text="{Binding Path=m1}" Foreground="LightSalmon" Style="{StaticResource Fixed}" Margin="0,0,6,8" />
<TextBlock Text="{Binding Path=n1}" Foreground="Orange" Style="{StaticResource Fixed}" Margin="0,0,6,8" />
<TextBlock Text="{Binding Path=m2}" Foreground="LightGreen" Style="{StaticResource Fixed}" Margin="0,0,6,8" />
<TextBlock Text="{Binding Path=m3}" Foreground="Green" Style="{StaticResource Fixed}" Margin="0,0,6,8" />
<TextBlock Text="{Binding ElementName=H1, Path=Items.Count,Mode=OneWay}" Style="{StaticResource Fixed}" Margin="0,0,6,8" />
</StackPanel>
<ItemsControl Name="ic" DockPanel.Dock="Bottom" ItemsSource="{Binding Path=seg}" ItemsPanel="{StaticResource HSPanel}">
<ControlTemplate TargetType="ItemsControl">
<Border>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</DockPanel>
</DataTemplate>
i tried below options but sorting is not working.
1.Tried sorting in constructor of the user control like following (code behind)
ic.Items.SortDescriptions.Clear();
ic.Items.SortDescriptions.Add(new SortDescription("id", ListSortDirection.Ascending));
ic.Items.Refresh();
But i am unable to access ic in code behind. Errors says "ic does not exists in current context"
2.Tried CollectionViewSource under ItemsControl in xaml which is also not working.
<ItemsControl x:Name="ic" DockPanel.Dock="Bottom" ItemsSource="{Binding Path=segments}" ItemsPanel="{StaticResource HSPanel}">
<ItemsControl.Resources>
<CollectionViewSource x:Key="segments" Source="{Binding seg}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="id" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</ItemsControl.Resources>
<ItemsControl.Template>
3.Tried CollectionViewSource under ControlTemplate in xaml which is also not working.
<ControlTemplate TargetType="ItemsControl">
<ControlTemplate.Resources>
<CollectionViewSource x:Key="segments" Source="{Binding seg}" >
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="sortId" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</ControlTemplate.Resources>
But i initialised Loaded event of ic and tried to do sorting from there.In this case initially when the page loads, the items are not sorted. But when i move to another user control and come back to this current user control, the items looks sorted out perfectly.
private void ic_Loaded(object sender, RoutedEventArgs e)
{
ItemsControl ic = (ItemsControl)sender;
ic.Items.SortDescriptions.Clear();
ic.Items.SortDescriptions.Add(new SortDescription("id", ListSortDirection.Ascending));
ic.Items.Refresh();
}
You have two options :
1 - Sort your source collection (seg) in View Model.
2 - Use CollectionViewSource (http://msdn.microsoft.com/fr-fr/library/system.windows.data.collectionviewsource.aspx).
Here is a full working exemple:
I have added this code in to an empty WPF window:
public class SomeVM
{
public ObservableCollection<SomeItemVM> Items { get; set; }
public SomeVM()
{
Items = new ObservableCollection<SomeItemVM>();
}
}
public class SomeItemVM
{
public string id { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//Create some VM
SomeVM data = new SomeVM();
data.Items.Add(new SomeItemVM() { id = "3" });
data.Items.Add(new SomeItemVM() { id = "4" });
data.Items.Add(new SomeItemVM() { id = "1" });
data.Items.Add(new SomeItemVM() { id = "2" });
this.DataContext = data;
}
}
Then in XAML I add a content control that will hold the VM and a DataTemplate that will describe the way the VM will be displayed:
<Window.Resources>
<DataTemplate x:Key="With">
<DockPanel>
<DockPanel.Resources>
<!-- CollectionViewSource should be declared as a resource of parent container of the ItemsControl.
Otherwise there will be an exception of StaticResourceHolder -->
<CollectionViewSource x:Key="segments" Source="{Binding Items}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="id" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</DockPanel.Resources>
<ItemsControl Name="ic" DockPanel.Dock="Bottom" ItemsSource="{Binding Source={StaticResource segments}}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding id}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource With}"/>
</Grid>
The resulting ItemsControl will display sorted items.
Finally i solved the sorting problem.
I am binding segments(observablecollection< seg>) to the itemscontrol. Previously In code behind i was generating segments directly like below
segments[0].name="GHI";
segments[0].age=40;
segments[1].name="ABC";
segments[1].age=20;
segments[2].name="DEF";
segments[2].age=30;
Instead of generating segments directly, i created another variable objSegments and generated the values like below.
objSegments[0].name="GHI";
objSegments[0].age=40;
objSegments[1].name="ABC";
objSegments[1].age=20;
objSegments[2].name="DEF";
objSegments[2].age=30;
After generating all the values, sorting is done and assigned to segments using the code below.
ObservableCollection<seg> sortedSegments = new ObservableCollection<seg>(objSegments.OrderBy(c => c.id));
foreach (var objSeg in sortedSegments)
{
segments.Add(objSeg);
}
It worked fine for me.

Using FindAncestor from within an Itemscontrol datatemplate to find a textblock outside of the datatemplate

I have a ItemsControl that's bound to an object, within the datatemplate of the ItemsControl i have two textblocks, I want to bind the first textblock's text property to another textblock that sits outside this ItemsControl.
I have tried finding the object in the parent datacontext and also simply trying to find the TextBlock with the Path=Text
one example is below :
<TextBlock Name="Name" Text="{Binding Name}"
Grid.Column="0"
FontSize="{DynamicResource SmallSize}"
TextWrapping="Wrap"
TextAlignment="Right"
Padding="4,0,0,0"
Grid.ColumnSpan="2" Background="Aqua"/>
<ItemsControl ItemsSource="{Binding TheValue}"
Padding="4,0,0,0"
Grid.Column="2"
HorizontalAlignment="Right">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text = "{
Binding RelativeSource =
{RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=Name}"
Grid.Column="0"
FontSize="{DynamicResource SmallSize}"
TextWrapping="Wrap" ........................
{Binding RelativeSource = {RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=Name}
Here you say to WPF find first parent of this control with Type Window, e.g. it's "ParentWindow". After this binding occurs to "ParentWindow" Name property.
If you want enable binding to control, which defined in same XAML, you can set the source explicitly by using Binding.ElementName property.
This is example for you code:
<TextBlock Text = "{Binding ElementName=Name, Path=Text}"/>
By the way, using control name as "Name" not is good. If you use this control form code behind it's looking as Name.Text = "some text", which can cause a trouble to understand what is going on.
UPDATE:
Example of binding to control DataContext Property in different datatemplate
class MainViewModel
{
public Class1 C1 { get; set; }
public Class2 C2 { get; set; }
public MainViewModel()
{
C1 = new Class1 { S1 = "This is C1 data context" };
C2 = new Class2 { S2 = "This is C2 data context" };
}
}
In XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MainViewModel}">
<StackPanel>
<ContentControl Name="cc1" Content="{Binding C1}"/>
<ContentControl Name="cc2" Content="{Binding C2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Class1}">
<TextBlock Text="{Binding S1}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Class2}">
<TextBlock Text="{Binding ElementName=cc1, Path=DataContext.C1.S1}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
But, I don't think something like this is a good approach. Especially, because this can be many items with this DataTemplate. Maybe you need delegate this to your ViewModel.

WPF Treeview - Binding to a collection with different depths, and styling differently

Apologies for the long post -- Read a few threads about this, but still find it hard to implement. Basically, I have a collection of objects, defined as:
public class LibData
{
public string Name { get; set; }
ObservableCollection<LibObject> _list;
public ObservableCollection<LibObject> List { get { return _list; } }
public LibData(string name, LibDataType type)
{
this.Name = name;
_list = new ObservableCollection<LibObject>();
}
}
and the object:
public class LibObject
{
public string Name { get; set; }
public LibObject(string name)
{
this.Name = name;
}
}
My main problem is in the XAML, and styling this TreeView. I need to have a specific style for a "root" item, and a specific style for a "leaf". Thing is, that one item in the bound list is "Root->Leaf", and another is "Root->Child->Leaf".
I tried this:
<TreeView x:Name="myTree" ItemsSource="{x:Static local:myDataList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=List}" >
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<CheckBox IsChecked="True" Content="HeaderCheckbox"/>
</StackPanel>
</Grid>
<HierarchicalDataTemplate.ItemTemplate >
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="True" Content="LeafCheckbox" />
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</Grid>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
This obviously works fine for the "Root->Leaf" item, but not for the "Root-Child-Leaf".
The XAML implementation seems to be more of a "hard coded" solution, where I know that the item layout is always "Root->Leaf" - how do I make this dynamic?
Again, I have seen solutions to different levels (including using converters), but the problem i'm having is that I need specific styles to root and leaf and nothing for levels in between.
I'm wondering if I'm looking at this completely wrong ...
Easy way to do this. Pull the DataTemplates out of the TreeView and put them in the resources section. Specify the DataType property for each DataTemplate, but do not include a key. The ItemTemplateSelector (a property of type DataTemplateSelector) on the TreeView will do the magic of using whichever DataTemplate suits the correct item type.
Create a HierarchicalDataTemplate for types Root and Child, and a DataTemplate for type Leaf.
Something like this:
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type local:Leaf}">
<Grid>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="True" Content="LeafCheckbox" />
<TextBlock Text="{Binding Path=SomeValue}"/>
</StackPanel>
</Grid>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Child}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Child " />
<TextBlock Text="{Binding Path=SomeValue}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Root}"
ItemsSource="{Binding Children}">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Root " />
<TextBlock Text="{Binding Path=SomeValue}" />
</StackPanel>
</Grid>
</HierarchicalDataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<TreeView x:Name="myTree" ItemsSource="{Binding}" />
</Grid>

Resources