如何在Treeview中的leafnode中再输入一个级别

时间:2014-02-20 11:28:12

标签: c# wpf treeview wpf-controls

我正在使用TreeView准备HierarchicalDataTemplate。它包含多个级别。但是,所有级别的数据结构都是相同类型的。 我在数据结构中有一个名为isNodeAleaf的属性。我将此属性设置为对所有叶级元素都为true。

我有一个HiearchicalDataTemplate设置用于填充树,如下所示。

<HierarchicalDataTemplate ItemsSource="{Binding VisibleChildren}">
   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition/>
         <RowDefinition/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
         <ColumnDefinition>
            <ColumnDefinition.Width>250</ColumnDefinition.Width>
         </ColumnDefinition>
      </Grid.ColumnDefinitions>
      <StackPanel Grid.Column="0">
         <Grid>
            <Grid.RowDefinitions>
               <RowDefinition/>
               <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Text="{Binding RiskName}" TextWrapping="WrapWithOverflow" />
         </Grid>
      </StackPanel>
   </Grid>
</HierarchicalDataTemplate>

它按预期填充树。现在,我需要将每个叶子附加一些属性。 这些属性主要是标记叶子节点等元素,以便在后期可以考虑。这有助于树中的许多元素,我们知道哪些是重要的元素。 我们将来可以将其他几个属性附加到每个叶元素上。

1
-|2
  -|3
    -|4 => leaf node
      -| prop1 -| prop2 -| prop3 -| prop4   
    -|5
      -| prop1 -| prop2 -| prop3 -| prop4   
    -|6
      -| prop1 -| prop2 -| prop3 -| prop4

现在我的问题是如何修改我的HiearchicalDataTemplate以获得上面显示的视觉样式。

如果社区中有人能帮助我,我将不胜感激。

3 个答案:

答案 0 :(得分:1)

使节点的属性显示为节点的子节点需要一些工作。最简单的方法是将属性枚举到列表中,因为TreeView仅适用于对象列表。

公开另一个IEnumerable属性。该属性返回一个包含属性的字符串表示形式的对象。

我在下面创建了一个简单的例子来说明我的意思。在其中,我使用反射创建一个新节点来显示属性信息,我通过IEnumerable类型的属性公开了该虚拟项。我也使用Style和DataTemplate来模仿HierarchicalDataTemplate。

在我的例子中,背后的代码是

namespace StackOverflow._21906388
{
    public class MyNode
    {
        public MyNode()
        {
            Items = new ObservableCollection<MyNode>();
            Property1 = "P1";
            Property2 = "P1";
        }

        public string Name { get; set; }

        public bool IsLeafNode { get; set; }

        [DisplayName("Property 1")]
        public string Property1 { get; set; }

        [DisplayName("Property 2")]
        public string Property2 { get; set; }

        public ObservableCollection<MyNode> Items { get; set; }

        public IEnumerable<MyNode> Properties
        {
            get
            {
                var list = new List<MyNode>();

                if (IsLeafNode)
                {
                    var nameBuffer = new StringBuilder();
                    this.GetType().GetProperties().Where(p => p.GetCustomAttribute<DisplayNameAttribute>() != null).ToList()
                        .ForEach(p => nameBuffer.Append(string.Format(" - | {0}: {1}", p.GetCustomAttribute<DisplayNameAttribute>().DisplayName, p.GetValue(this))));
                    list.Add(new MyNode() { Name = nameBuffer.ToString() });
                }
                return list;
            }
        }

    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitialiseData();

            InitializeComponent();
        }

        public ObservableCollection<MyNode> Items { get; set; }

        public void InitialiseData()
        {
            Items = new ObservableCollection<MyNode>
            {
                new MyNode
                {
                    Name = "one",
                    Items = new ObservableCollection<MyNode>
                    {
                        new MyNode { Name = "one", IsLeafNode = true },
                        new MyNode { Name = "two", IsLeafNode = true },
                        new MyNode { Name = "three", IsLeafNode = true }
                    }
                },
                new MyNode
                {
                    Name = "two",
                    Items = new ObservableCollection<MyNode>
                    {
                        new MyNode { Name = "one", IsLeafNode = true },
                        new MyNode { Name = "two", IsLeafNode = true },
                        new MyNode { Name = "three", IsLeafNode = true }
                    }
                },
                new MyNode { Name = "three", IsLeafNode = true }
            };
        }
    }
}

windows xaml是

<Window x:Class="StackOverflow._21906388.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:this="clr-namespace:StackOverflow._21906388"
        DataContext="{Binding RelativeSource={RelativeSource Self}}" Title="MainWindow" Height="350" Width="525">

    <Window.Resources>

        <DataTemplate DataType="{x:Type this:MyNode}">
            <TextBlock Text="{Binding Path=Name}" />
        </DataTemplate>        

        <Style TargetType="{x:Type TreeViewItem}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=IsLeafNode}" Value="True">
                    <Setter Property="ItemsSource" Value="{Binding Path=Properties}" />
                </DataTrigger>
            </Style.Triggers>
            <Setter Property="ItemsSource" Value="{Binding Path=Items}" />
        </Style>
    </Window.Resources>

    <Grid>
        <TreeView ItemsSource="{Binding Path=Items}" />
    </Grid>
</Window>

您可以将此代码复制到空的WPF应用程序项目中以查看它是否运行。

您可以更改生成虚拟节点的方式(以及何时),将虚拟节点插入子列表,然后实现HierarchicalDataTemplate以遍历对象图而不是。

我希望这会有所帮助。

答案 1 :(得分:0)

有两种方法可以做到这一点,但最简单的方法是使用ItemsControl并插入一个水平方向的StackPanel作为其ItemsPanel。例如,在HierarchicalDataTemplate中,将ItemsControl插入到具有TextBlock的Grid的第二行,如此。

 <Grid>
    <Grid.RowDefinitions>
       <RowDefinition/>
       <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Text="{Binding RiskName}" TextWrapping="WrapWithOverflow" />
    <ItemsControl ItemsSource="{Binding Path=Properties}" Visibility="{Binding Path=isNodeAleaf, Converter={StaticResource BooleanToVisibility}}>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
 </Grid>

您必须包含BooleanToVisibilityConverter资源才能显示叶子的“属性”列表。

如果您发现StackPanel有许多项目,请将其替换为WrapPanel

关于属性的显示,只需通过ItemsControl的ItemTemplate属性模板化每个项目。

此答案假定您希望属性显示在与叶元素相同的节点中。如果您希望属性显示为叶元素的子节点,请通过评论告诉我,我将为该方案发布答案​​。

我希望这会有所帮助。

答案 2 :(得分:0)

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:this="clr-namespace:WpfApplication1"
        DataContext="{Binding RelativeSource={RelativeSource Self}}" Title="MainWindow" Height="350" Width="525">

    <Window.Resources>

        <DataTemplate DataType="{x:Type this:MyNode}">
            <TextBlock Text="{Binding Path=Name}" />
        </DataTemplate>

        <Style TargetType="{x:Type TreeViewItem}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=IsLeafNode}" Value="True">
                    <Setter Property="ItemsSource" Value="{Binding Path=Properties}" />
                    <Setter Property="ItemsPanel">
                        <Setter.Value>
                            <ItemsPanelTemplate>
                                <StackPanel HorizontalAlignment="Left"  IsItemsHost="True" Orientation="Horizontal"  />
                            </ItemsPanelTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
            <Setter Property="ItemsSource" Value="{Binding Path=Items}" />

        </Style>
    </Window.Resources>

    <Grid>
        <TreeView ItemsSource="{Binding Path=Items}" />
    </Grid>
</Window>

捕获所有显示名称的代码。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApplication1
{
    public class MyNode
    {
        public MyNode()
        {
            Items = new ObservableCollection<MyNode>();
            Property1 = "P1";
            Property2 = "P1";
        }

        public string Name { get; set; }

        public bool IsLeafNode { get; set; }

        [DisplayName("Property 1")]
        public string Property1 { get; set; }

        [DisplayName("Property 2")]
        public string Property2 { get; set; }

        public ObservableCollection<MyNode> Items { get; set; }

        public IEnumerable<MyNode> Properties
        {
            get
            {
                var list = new List<MyNode>();


                if (IsLeafNode)
                {
                    var nameBuffer = new StringBuilder();

                    Type type = new MyNode().GetType();

                    foreach (PropertyInfo pInfo in type.GetProperties())
                    {
                        DisplayNameAttribute attr = (DisplayNameAttribute)Attribute.GetCustomAttribute(pInfo, typeof(DisplayNameAttribute));

                        if (attr != null)
                        {
                            list.Add(new MyNode() { Name = attr.DisplayName.ToString() });

                        }
                    }

                }
                return list;
            }
        }

    }
}

mainwindow.xaml.cs

// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitialiseData();

            InitializeComponent();
        }

        public ObservableCollection<MyNode> Items { get; set; }

        public void InitialiseData()
        {
            Items = new ObservableCollection<MyNode>
            {
                new MyNode
                {
                    Name = "one",
                    Items = new ObservableCollection<MyNode>
                    {
                        new MyNode { Name = "one", IsLeafNode = true },
                        new MyNode { Name = "two", IsLeafNode = true },
                        new MyNode { Name = "three", IsLeafNode = true }
                    }
                },
                new MyNode
                {
                    Name = "two",
                    Items = new ObservableCollection<MyNode>
                    {
                        new MyNode { Name = "one", IsLeafNode = true },
                        new MyNode { Name = "two", IsLeafNode = true },
                        new MyNode { Name = "three", IsLeafNode = true }
                    }
                },
                new MyNode { Name = "three", IsLeafNode = true }
            };
        }
    }