How can I have multiple types of children in a single Silverlight TreeView node?

邮差的信 提交于 2019-12-22 06:47:32

问题


Short Vesion:

I have to display a hierarchy (TreeView) of items of different types, and am not sure how to do this cleanly in Silverlight. In WPF, it's straightforward to define templates (HierarchicalDataTemplate) based on types, but this feature isn't available in Silverlight. It seems in Silverlight you have to apply the same template to all children of a specific node, so you end up with once single monster template that handles every possible type of node, applied to every single node.

Long Version (with example):

To give a more concrete (but contrived) example, consider a treeview of archives in various folders, where each archive can contain photos, songs, and other archives. Each folder may contain several subfolders and archives.

|-Folder
  |-Folder
    |-Folder
      |-Archive
        | Photo1
        | Photo2
        | Song1
        | Song2
        |-Archive
          | Photo1
          | Song1
  |-Archive
    | Photo1
    | Photo2
    | Photo3

Each type in the tree (Folder, Archive, Photo, Song) is displayed differently. The obvious solution seemed to be to create a HierarchicalDataTemplate for each type of item to display. Unfortunately, I can't find a good way to do this, because it seems that you have to specify a single template type for all of the children of a node (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!


回答1:


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.




回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/4318206/how-can-i-have-multiple-types-of-children-in-a-single-silverlight-treeview-node

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!