Как я могу иметь несколько типов детей в одном узле Silverlight TreeView? - PullRequest
5 голосов
/ 30 ноября 2010

Короткая версия:

Мне нужно отобразить иерархию (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, показывающий:

Для песен: прокручиваемое текстовое поле с именем исполнителя / названием и кнопкой воспроизведения

Для изображений: миниатюра и элемент управления рейтингом

Для архивов: изображение архива с индикатором выполнения, показывающим сжатие

Для папок: простая метка с именем папки

Насколько я могу судить, единственный способ добиться этого - иметь 1 гигантский шаблон HierarchicalDataTemplate, содержащий текстовое поле с прокруткой, кнопку воспроизведения, средство просмотра миниатюр, элемент управления звездочкой, элемент управления изображением, индикатор выполнения и метку. , Затем я бы просто выборочно скрыл все, кроме одного или двух элементов управления, которые фактически применяются к узлу.

В WPF я мог связать шаблоны с типом узла, чтобы каждый узел мог использовать соответствующий шаблон. Мне интересно, есть ли способ сделать это в Silverlight.

Еще раз спасибо!

Ответы [ 3 ]

3 голосов
/ 01 декабря 2010

Ну, почему бы вам не попробовать что-нибудь подобное?

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 или что-то еще), которые будут реализованы каждым типом подкласса (Архив, Фото, Музыка).

Надеюсь, это поможет.

0 голосов
/ 21 февраля 2013

У меня была проблема, похожая на эту, когда у меня было 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

Вот XAML для использованного мной HierarchicalDataTemplate, который реализует 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 и т. Д.

0 голосов
/ 30 ноября 2012

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

...