使用不同类型的多个层次结构数据模板更改IsSelected上树视图中的HeaderTemplate

本文关键字:IsSelected 视图 HeaderTemplate 同类型 数据 层次结构 | 更新日期: 2023-09-27 18:25:37

我有一个TreeView,我想向它添加两种不同的节点类型,每个类型都有自己的HierachicalDataTemplate。我有这个工作(代码如下)

我想要的是,当树中的任何节点被选中时,我希望该节点的模板发生变化,为BoolNode节点使用不同的模板,为CompareNodes使用不同的模版。我发现了一些使用Styles和Trigger的示例,但它们都适用于TreeView,其中所有节点共享相同的模板。

TreeView Xaml:

    <TreeView Name="m_kTest">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type self:BoolNode}" ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding OpText}"/>
                </StackPanel>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type self:CompareNode}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Header}"/>
                    <TextBlock Text=" "/>
                    <TextBlock Text="{Binding OpText}"/>
                    <TextBlock Text=" "/>
                    <TextBlock Text="{Binding Value}"/>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>

IQueryNode:

public interface IQueryNode
{
    ObservableCollection<IQueryNode> Children { get; }
    int OpIndex { get; set; }
    String OpText{get;}
}

BoolNode:

public class BoolNode :IQueryNode
{
    public int OpIndex { get; set; }
    public String OpText { get { ... } }
    public ObservableCollection<IQueryNode> Children { get; private set; }
    public BoolNode()
    {
        Children = new ObservableCollection<IQueryNode>();
    }
}

CompareNode:

public class CompareNode: IQueryNode
{
    public ObservableCollection<IQueryNode> Children { get; private set; }
    public int OpIndex { get; set; }
    public String OpText {get {...} }
    public String Header { get; set; }
    public String Value { get; set; }
    public CompareNode()
    {
        Children = new ObservableCollection<IQueryNode>();
    }
}

使用不同类型的多个层次结构数据模板更改IsSelected上树视图中的HeaderTemplate

我以一种"黑客"的方式完成了这项工作。

我在两个类中添加了一个"IsSelected"属性,并将其绑定到TreeViewNode的IsSelected属性。然后,由于我只使用了两种数据类型,我添加了一个"IsBoolean"布尔字段,并触发了对这两个值的模板更改:

TreeView Xaml:

    <TreeView Name="m_kTest">
        <TreeView.Resources>
            <HierarchicalDataTemplate x:Key="BoolDisplayTemplate" DataType="{x:Type self:BoolNode}" ItemsSource="{Binding Children}"> /*template*/ </HierarchicalDataTemplate>
            <HierarchicalDataTemplate x:Key="BoolEditTemplate" DataType="{x:Type self:BoolNode}" ItemsSource="{Binding Children}"> /*template*/ </HierarchicalDataTemplate>
            <HierarchicalDataTemplate x:Key="CompareEditTemplate"  DataType="{x:Type self:CompareNode}" ItemsSource="{Binding Children}"> /*template*/ </HierarchicalDataTemplate>
            <HierarchicalDataTemplate x:Key="CompareDisplayTemplate" DataType="{x:Type self:CompareNode}" ItemsSource="{Binding Children}"> /*template*/ </HierarchicalDataTemplate>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=IsBoolNode}" Value="True"/>
                        <Condition Binding="{Binding Path=IsSelected}" Value="False"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Value="{StaticResource BoolDisplayTemplate}" Property="HeaderTemplate"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=IsBoolNode}" Value="True"/>
                        <Condition Binding="{Binding Path=IsSelected}" Value="True"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Value="{StaticResource BoolEditTemplate}" Property="HeaderTemplate"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=IsBoolNode}" Value="False"/>
                        <Condition Binding="{Binding Path=IsSelected}" Value="False"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Value="{StaticResource CompareDisplayTemplate}" Property="HeaderTemplate"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=IsBoolNode}" Value="False"/>
                        <Condition Binding="{Binding Path=IsSelected}" Value="True"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Value="{StaticResource CompareEditTemplate}" Property="HeaderTemplate"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
        </TreeView.Resources>
    </TreeView>

IQueryNode:

public interface IQueryNode
{
    ObservableCollection<IQueryNode> Children { get; }
    int OpIndex { get; set; }
    String OpText{get;}
    bool IsBoolNode { get; }
    bool IsSelected { get; set; }
}

BoolNode:

public class BoolNode :IQueryNode
{
    public int OpIndex { get; set; }
    public String OpText { get { ... } }
    public ObservableCollection<IQueryNode> Children { get; private set; }
    public bool IsBoolNode{get{return true;}}
    public bool IsSelected { get; set;}
    public BoolNode()
    {
        OpIndex = 0;
        Children = new ObservableCollection<IQueryNode>();
        IsSelected = false;
    }
}

CompareNode:

public class CompareNode: IQueryNode
{
    public ObservableCollection<IQueryNode> Children { get; private set; }
    public int OpIndex { get; set; }
    public String OpText{ get{ ... } }
    public String Header { get; set; }
    public String Value { get; set; }
    public bool IsBoolNode { get { return false; } }
    public bool IsSelected { get; set; }
    public CompareNode()
    {
        Children = new ObservableCollection<IQueryNode>();
        IsSelected = false;
    }
}

以下是我只在选定项目上获得自定义样式的方法

XML:

<Window x:Name="window" 
    x:Class="stackoverflowTreeview.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:t="clr-namespace:System;assembly=mscorlib"
    xmlns:this="clr-namespace:stackoverflowTreeview"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <this:testConv x:Key="testConv"/>
    <Style TargetType="TreeView">
        <Setter Property="ItemTemplate">
            <Setter.Value>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <ContentControl>
                        <ContentControl.Style>
                            <Style>
                                <Setter Property="ContentControl.Content">
                                    <Setter.Value>
                                        <!-- This is the default, common template -->
                                        <TextBlock Text="{Binding Name, Converter={StaticResource testConv}}"/>
                                    </Setter.Value>
                                </Setter>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="True">
                                        <Setter Property="ContentControl.Content">
                                            <Setter.Value>
                                                <ContentControl Content="{Binding}">
                                                    <ContentControl.Resources>
                                                        <!-- These templates are type specific, change them for your desired types -->
                                                        <DataTemplate DataType="{x:Type this:Herp}">
                                                            <TextBlock Text="{Binding Name}"/>
                                                        </DataTemplate>
                                                        <DataTemplate DataType="{x:Type this:Derp}">
                                                            <StackPanel>
                                                                <TextBlock Text="{Binding Name}"/>
                                                                <TextBlock Text="{Binding Value}"/>
                                                            </StackPanel>
                                                        </DataTemplate>
                                                    </ContentControl.Resources>
                                                </ContentControl>
                                            </Setter.Value>
                                        </Setter>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </ContentControl.Style>
                    </ContentControl>
                </HierarchicalDataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
    <Grid>
        <TreeView Name="m_kTest" ItemsSource="{Binding Data, ElementName=window}">
        </TreeView>
    </Grid>
</Window>

C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
using System.Collections;
namespace stackoverflowTreeview
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Data = new List<IHerp>()
            {
                new Derp("Derp Root", "Derp Root Value")  
                { 
                    Children = new List<IHerp>() 
                    { 
                        new Herp("Herp Child")
                        { 
                            Children =  new List<IHerp>() {new Derp("Derp Grandchild","Derp GrandChild Value")}
                        },
                        new Derp("Derp Child2", "Derp Child2 Value")
                        { 
                            Children =  new List<IHerp>() {new Derp("Derp Grandchild","Derp GrandChild Value")}
                        },
                        new Herp("Herp Child")
                        { 
                            Children =  new List<IHerp>() {new Derp("Derp Grandchild","Derp GrandChild Value")}
                        }
                    }
                }
            };
        }
        public static DependencyProperty dData = DependencyProperty.Register("Data", typeof(List<IHerp>), typeof(MainWindow));
        public List<IHerp> Data
        {
            get { return (List<IHerp>)GetValue(dData); }
            set { SetValue(dData, value); }
        }
    }
    public abstract class IHerp : DependencyObject
    {
        public static DependencyProperty dChildren = DependencyProperty.Register("Children", typeof(List<IHerp>), typeof(IHerp));
        public List<IHerp> Children { get { return (List<IHerp>)GetValue(dChildren); } set { SetValue(dChildren, value); } }
        public static DependencyProperty dName = DependencyProperty.Register("Name", typeof(string), typeof(IHerp));
        public string Name { get{return (string)GetValue(dName);} set{SetValue(dName,value);} }
        public IHerp()
        {
            Children = Children == null ? new List<IHerp>() : Children;
            Name = Name == null ? "" : Name;
        }
    }
    public class Herp : IHerp
    {
        public Herp(string name)
        {
            Name = name;
        }
    }
    public class Derp : IHerp
    {
        public string Value { get; set; }
        public Derp(string name, string value)
        {
            Name = name;
            Value = value;
        }
    }
    public class testConv : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            try
            {
                return value;
            }
            catch { return typeof(object); }
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}