5

短版:

我必须显示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}) 的所有子节点指定一个模板类型。

This requirement causes the template to snowball... an archive can have Photos, Songs, and Archives as children. Because a single template must be applied to all children, that one template must be able to handle Photos, Songs, and Archives. Similarly, a Folder's template must be able to handle Folders and Archives, and the Archive template now has Photos and Songs stuck in it, so it all ends up as one giant template that can handle Photos, Songs, Archives, and Folders. As more types are added, they also get lumped into the one huge template.

Is there any way to do this cleanly, without accumulating one giant template (and associated node viewmodel) as different types are added to the tree?

Thanks!

Some Clarification:

Thanks for the answers so far, but I think they may just lead me back to the original issue. I may be misunderstanding the answer.

Consider the TreeView showing:

For Songs: a scrolling textbox with artist/title, and a play button

For Pictures: a thumbnail image, and a star rating control

For Archives: An archive image, with a progress bar showing the compression

For Folders: A plain label showing the folder name

As far as I can tell, the only way to achieve this is to have 1 giant HierarchicalDataTemplate containing a scrolling textbox, a play button, a thumbnail viewer, a star control, an image control, a progress bar, and a label. I'd then just selectively hide all but the one or two controls that actually apply to the node.

In WPF I could associate templates with a node type, so each node could use an appropriate template. I'm wondering if there's a way to do this in Silverlight.

Thanks again!

4

3 回答 3

3

Well, why don't you try something like this?

HierarchicalDataTemplate

<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>

Node Class

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);
    }
}

Example Data

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;
    }
}

In your case maybe could help an interface (INode for example) that has all the properties for styling nodes (like ForegroundColor, or whatever) that will be implemented by each type of subclass (Archive, Photo, Music).

Hope this helps.

于 2010-11-30T21:19:33.240 回答
0

In Silverlight 5 we can also solve this by using the implicit data templates:

<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}"/>

Since Silverlight 5 still does not support automatic selection of an appropriate HierarchicalDataTemplate itself, depending on it's target type, we use the single HierarchicalDataTemplate for all types of nodes. Thus, we still need each our node view model to contain the same Children member.

于 2012-11-30T15:34:42.333 回答
0

I had a similar issue to this whereby I had a TreeView with multiple node types and wanted to be able to choose a different template based on the Node Type.

In the end I came across a TemplateChooser and used that in conjunction with HierarchicalDataTemplates. (Please forgive the fact the code is in 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

I then created a specific Template Selector for the tree view, which exposed a different Data Template based on the object type.

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

Here's the XAML for the HierarchicalDataTemplate I used which implements the 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>

I then of course made up some hierarchicaldatatemplates for the various types, e.g. MynodeType1HierarchicalTemplate and so on.

于 2013-02-21T11:35:40.777 回答