我正在使用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
以获得上面显示的视觉样式。
如果社区中有人能帮助我,我将不胜感激。
答案 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 }
};
}
}