Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 972
James Barber Avatar asked Oct 25 '22 01:10

James Barber


1 Answers

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.

like image 125
as-cii Avatar answered Nov 14 '22 00:11

as-cii