Short Vesion:
我必须显示不同类型的项目的层次结构(TreeView
),并且我不确定如何在Silverlight中干净地执行此操作。在WPF中,可以直接根据类型定义模板(HierarchicalDataTemplate
),但Silverlight中不提供此功能。在Silverlight中,您必须将相同的模板应用于特定节点的所有子节点,因此您最终会得到一个处理每种可能类型节点的单个怪物模板,应用于每个节点。
长版(带示例):
为了给出一个更具体(但是做作)的例子,考虑各种文件夹中的档案树视图,其中每个档案可以包含照片,歌曲和其他档案。每个文件夹可能包含多个子文件夹和存档。
|-Folder
|-Folder
|-Folder
|-Archive
| Photo1
| Photo2
| Song1
| Song2
|-Archive
| Photo1
| Song1
|-Archive
| Photo1
| Photo2
| Photo3
树中的每种类型(文件夹,存档,照片,歌曲)的显示方式都不同。显而易见的解决方案似乎是为要显示的每种类型的项目创建HierarchicalDataTemplate
。不幸的是,我找不到一个好方法,因为你似乎必须为节点的所有子节点(ItemsSource={Binding ...}, ItemsTemplate={StaticResource TemplateForAllChildren}
)指定一个模板类型。
此要求导致模板滚雪球......存档可以将照片,歌曲和档案作为子项。由于必须将单个模板应用于所有子项,因此该模板必须能够处理照片,歌曲和档案。同样,文件夹的模板必须能够处理文件夹和档案,而档案模板现在卡在其中的照片和歌曲,所以它最终成为一个可以处理照片,歌曲,档案和文件夹的巨型模板。随着更多类型的添加,它们也会被集中到一个巨大的模板中。
有没有办法干净地完成这项工作,而不会累积一个巨型模板(和相关的节点视图模型),因为不同的类型被添加到树中?
谢谢!
一些澄清:
感谢目前为止的答案,但我认为他们可能会让我回到最初的问题。我可能会误解答案。
考虑TreeView显示:
对于歌曲:带有艺术家/标题的滚动文本框和播放按钮
对于图片:缩略图图像和星级评分控件
For Archives:存档图像,进度条显示压缩
对于文件夹:显示文件夹名称
的普通标签据我所知,实现这一目标的唯一方法是拥有1个巨大的HierarchicalDataTemplate,其中包含滚动文本框,播放按钮,缩略图查看器,星形控件,图像控件,进度条和标签。然后,我只是选择性地隐藏除了实际应用于节点的一个或两个控件之外的所有控件。
在WPF中,我可以将模板与节点类型相关联,因此每个节点都可以使用适当的模板。我想知道在Silverlight中是否有办法做到这一点。
再次感谢!
答案 0 :(得分:3)
那么,你为什么不尝试这样的事情?
<sdk:HierarchicalDataTemplate x:Key="ChildTemplate" ItemsSource="{Binding Path=SubItems}">
<TextBlock Text="{Binding Name}" Foreground="{Binding ForegroundColor}" />
</sdk:HierarchicalDataTemplate>
<sdk:HierarchicalDataTemplate x:Key="FilesDataTemplate" ItemsSource="{Binding Path=SubItems}" ItemTemplate="{StaticResource ChildTemplate}">
<TextBlock Text="{Binding Name}" Foreground="{Binding ForegroundColor}" />
</sdk:HierarchicalDataTemplate>
public class Node
{
public string Name { get; set; }
public ObservableCollection<Node> SubItems { get; set; }
public SolidColorBrush ForegroundColor { get; set; }
public Node(string name, Color foregroundColor, params Node[] items)
{
this.Name = name;
this.SubItems = new ObservableCollection<Node>(items);
this.ForegroundColor = new SolidColorBrush(foregroundColor);
}
}
public partial class MainPage : UserControl
{
public ObservableCollection<Node> Nodes { get; set; }
public MainPage()
{
InitializeComponent();
this.Nodes = new Node("Root", Colors.Blue,
new Node("File1", Colors.Black),
new Node("File2", Colors.Black),
new Node("Archive1", Colors.Red,
new Node("File3", Colors.Magenta),
new Node("File4", Colors.Magenta))
).SubItems;
treeView1.DataContext = this;
}
}
在您的情况下,也许可以帮助一个接口(例如INode)具有样式节点的所有属性(如ForegroundColor或其他),这些属性将由每种类型的子类(存档,照片,音乐)实现。
希望这有帮助。
答案 1 :(得分:0)
在Silverlight 5中,我们也可以使用隐式数据模板来解决这个问题:
<UserControl.Resources>
<sdk:HierarchicalDataTemplate x:Key="treeNodeTemplate"
ItemsSource="{Binding Children}">
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="ViewModels:Folder">
<TextBlock Text="{Binding FolderName}" />
</DataTemplate>
<DataTemplate DataType="ViewModels:Song">
<Image Source="{Binding PictureSource}" />
</DataTemplate>
...
</ContentControl.Resources>
</ContentControl>
</sdk:HierarchicalDataTemplate>
</UserControl.Resources>
<sdk:TreeView ItemsSource="{Binding Roots, Mode=OneWay}"
ItemTemplate="{StaticResource treeNodeTemplate}"/>
由于Silverlight 5仍然不支持自动选择适当的HierarchicalDataTemplate本身,因此根据它的目标类型,我们对所有类型的节点使用单个HierarchicalDataTemplate。因此,我们仍然需要每个节点视图模型包含相同的Children成员。
答案 2 :(得分:0)
我遇到了类似的问题,我有一个包含多种节点类型的TreeView,并希望能够根据节点类型选择不同的模板。
最后,我遇到了一个TemplateChooser,并将其与HierarchicalDataTemplates结合使用。 (请原谅代码在VB中的事实)
Public MustInherit Class TemplateSelector
Inherits ContentControl
Public MustOverride Function SelectTemplate(item As Object, container As DependencyObject) As DataTemplate
Protected Overrides Sub OnContentChanged(oldContent As Object, newContent As Object)
MyBase.OnContentChanged(oldContent, newContent)
ContentTemplate = SelectTemplate(newContent, Me)
End Sub
End Class
然后我为树视图创建了一个特定的模板选择器,它根据对象类型公开了不同的数据模板。
Public Class NodeTypeTemplateSelector
Inherits TemplateSelector
Public Property NodeType1Template As DataTemplate
Public Property NodeType2Template As DataTemplate
Public Property NodeType3Template As DataTemplate
Public Overrides Function SelectTemplate(item As Object, container As System.Windows.DependencyObject) As System.Windows.DataTemplate
If item.GetType.Equals(GetType(NodeType1VM)) Then
Return NodeType1Template
ElseIf item.GetType.Equals(GetType(NodeType2VM)) Then
Return NodeType2Template
ElseIf item.GetType.Equals(GetType(NodeType3VM)) Then
Return NodeType3Template
Else
Return Nothing
End If
End Function
End Class
这是我使用的HierarchicalDataTemplate的XAML,它实现了TemplateSelector。
<sdk:HierarchicalDataTemplate x:Key="SelectingTemplate" ItemsSource="{Binding children, Mode=OneWay}">
<local:NodeTypeTemplateSelector Content="{Binding}"
NodeType1Template="{StaticResource MyNodeType1HierarchicalTemplate}"
NodeType2Template="{StaticResource MyNodeType2HierarchicalTemplate}"
NodeType3Template="{StaticResource MyNodeType3HierarchicalTemplate}"
/>
</sdk:HierarchicalDataTemplate>
然后,我当然为各种类型制作了一些分层数据,例如: MynodeType1HierarchicalTemplate等等。