如何在单个 Silverlight TreeView 节点中拥有多种类型的子节点?

发布于 2024-10-05 10:22:49 字数 1596 浏览 3 评论 0原文

简短版本:

我必须显示不同类型项目的层次结构 (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:

对于歌曲:带有艺术家/标题的滚动文本框和一个播放按钮

对于图片:缩略图和星级评级控件

对于档案:存档图像,带有显示压缩的进度条

对于文件夹:A显示文件夹名称的普通标签

据我所知,实现此目的的唯一方法是拥有 1 个巨大的 HierarchicalDataTemplate,其中包含滚动文本框、播放按钮、缩略图查看器、星形控件、图像控件、进度条,和一个标签。然后,我将有选择地隐藏除实际应用于该节点的一两个控件之外的所有控件。

在 WPF 中,我可以将模板与节点类型相关联,因此每个节点都可以使用适当的模板。我想知道是否有办法在 Silverlight 中做到这一点。

再次感谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

脸赞 2024-10-12 10:22:49

那么,你为什么不尝试这样的事情呢?

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>

节点类

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 等)的所有属性,这些属性将由每种类型的子类(存档、照片、音乐)实现。

希望这有帮助。

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.

喵星人汪星人 2024-10-12 10:22:49

在 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 成员。

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.

莫言歌 2024-10-12 10:22:49

我遇到了与此类似的问题,我有一个具有多种节点类型的 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 等。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文