Wpf – Change TreeViewItem Template when IsSelected and two types using in TreeView

datatemplatehierarchicaldatatemplatetreeviewwpf

In my TreeView I use two differnt classes for binding. For example, I have a Group what can have ChildGroup and can have Items.
Example code of this classes:

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace WpfApplication1
{
public class Group
{
    public Group(string name)
    {
        Name = name;
        items = new ObservableCollection<Item>();
        groups = new ObservableCollection<Group>();
    }
    public string Name { get;
        set;
    }

    private ObservableCollection<Item> items;
    private ObservableCollection<Group> groups;

    public ObservableCollection<Item> Items
    {
        get { return items; }
    }


    public ObservableCollection<Group> Groups
    {
        get { return groups; }
    }

    public IEnumerable<object> AllItems
    {
        get
        {
            foreach (var group in groups)
            {
                yield return group;
            }
            foreach (var item in items)
            {
                yield return item;
            }
        }
    }

}

public class Item
{
    public Item(string name)
    {
        ItemName = name;
    }

    public string ItemName
    {
        get;
        set;
    } 
}
}

To bind it to TreeView I use following template

<Grid>
    <TreeView Name="treeView">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type WpfApplication1:Group}"
                                      ItemsSource="{Binding AllItems}">
                <TextBlock Text="{Binding Name}"/>
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type WpfApplication1:Item}">
                <TextBlock Text="{Binding ItemName}" FontStyle="Italic"/>
            </DataTemplate>
        </TreeView.Resources>
    </TreeView>
</Grid>

It is easy.

The problem is that I need to change ItemTemplate when Is selected. And I need to change only then Item class selected.

I can do it if only one class use for binding. It also easy using Style and Trigger, like this:

<TreeView Name="treeView1" Grid.Column="1">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type WpfApplication1:Group}"
                                      ItemsSource="{Binding AllItems}"
                                      x:Key="groupTemplate">
                <TextBlock Text="{Binding Name}"/>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type WpfApplication1:Group}"
                                      ItemsSource="{Binding AllItems}"
                                      x:Key="selectedGroupTemplate">
                <TextBlock Text="{Binding Name}" FontStyle="Italic" FontWeight="Bold" FontSize="14"/>
            </HierarchicalDataTemplate>
        </TreeView.Resources>

        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="HeaderTemplate" Value="{StaticResource groupTemplate}"/>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="HeaderTemplate" Value="{StaticResource selectedGroupTemplate}"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>

But I have a trouble for multiclass binding.

How can I change SelectedItem template then multiclass binding using? Any ideas?

My code behind sample:

using System.Collections.ObjectModel;
using System.Windows;

namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class Window2 : Window
{
    private ObservableCollection<Group> _groups;
    public ObservableCollection<Group> Groups
    {
        get { return _groups; }
    }

    public Window2()
    {
        InitializeComponent();

        InitGroups();

        treeView.ItemsSource = _groups;
        treeView1.ItemsSource = _groups;
    }

    private void InitGroups()
    {
        _groups = new ObservableCollection<Group>();

        Group group1 = new Group("Group1");
        group1.Groups.Add(new Group("Group1.1"));
        group1.Groups.Add(new Group("Group1.2"));
        group1.Groups.Add(new Group("Group1.3"));

        group1.Items.Add(new Item("Item1.1"));
        group1.Items.Add(new Item("Item1.2"));

        group1.Groups[1].Items.Add(new Item("Item1.2.1"));
        group1.Groups[1].Items.Add(new Item("Item1.2.2"));


        _groups.Add(group1);

        Group group2 = new Group("Group2");
        group2.Groups.Add(new Group("Group2.1"));
        group2.Groups.Add(new Group("Group2.2"));

        group2.Items.Add(new Item("Item2.1"));
        group2.Groups[0].Items.Add(new Item("Item2.1.1"));
        group2.Groups[0].Items.Add(new Item("Item2.1.1"));

        _groups.Add(group2);
    }
}
}

Result
Result

Now I think to use TreeView.HeaderTemplateSelector, but may be exists way to use only xaml.

Thanks.

Best Answer

There are a number of ways to acheive your desired result. If you are sure that your DataTemplate will only be used in TreeViewItem objects, then the easiest is simply to bind directly to the TreeViewItem.IsSelected property and then react to the change in your DataTemplate:

    <DataTemplate DataType="{x:Type WpfApplication1:Item}">
        <TextBlock Text="{Binding ItemName}">
            <TextBlock.Style>
                <Style>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsSelected, RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}, FallbackValue=False}" 
Value="True">
                            <Setter Property="TextBlock.FontStyle" Value="Italic" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBlock.Style>
        </TextBlock>
    </DataTemplate> 
Related Topic