Как создать древовидное представление для List<List<int>?

У меня есть класс EntityDisplay, который содержит 2 свойства:

public sealed class EntityDisplayObject
{

    public ICollection Collection { get; private set; }

    public string CollectionName { get; private set; }

    public EntityDisplayObject(ICollection collection)
    {
        this.Collection = collection;

        this.CollectionName = $"{this.Collection.GetType().Name [{this.Collection.Count}]";
    }

}

Иногда Коллекция будет List<int>, а иногда List<List<int>.

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

Это мой xaml на данный момент:

<Grid x:Uid = "Grid_4">

    <TreeView x:Uid = "TreeView_2"
              Background = "Transparent"
              BorderThickness = "0"
              Padding = "0">

        <TreeViewItem x:Uid = "TreeViewItem_1"
                      Header = "{Binding CollectionName}">

            <ListBox x:Uid = "ListBox_2" MouseDoubleClick = "TreeViewItem_MouseDoubleClick"
                     ItemsSource = "{Binding Collection}"
                     Background = "Transparent"
                     BorderThickness = "0">
            </ListBox>

        </TreeViewItem>

    </TreeView>
</Grid>

Простой список, работающий с xaml

Для вложенных списков я получаю заголовок древовидного представления и возможность расширения, но вместо того, чтобы показывать список на первом снимке экрана, он дает мне (Коллекция). На данный момент вложенный список

Смотрите этот ответ stackoverflow.com/a/78556341/3392605

Mustafa Mutasim 07.06.2024 11:47

Сосредоточьтесь на структуре Class_ParentChild_Model, вы ее получите, или опубликуйте свой class code для simple List<string>

Mustafa Mutasim 07.06.2024 11:48

Итак... вам нужно иерархическое древовидное представление? например. stackoverflow.com/questions/1912481

Rand Random 07.06.2024 12:06

@RandRandom спасибо, что помогло! В конце мне нужно было извлечь все элементы внутри коллекции и добавить их в новый список (это делается внутри некоторых проверок if, чтобы убедиться, что в коллекции есть элементы внутри, а затем, если какой-либо из этих элементов также является коллекцией , он повторит процесс. Наконец, в xaml внутри древовидного представления я привязываю элемент управления itemsControl к этому новому списку.

Arthur Rodrigues 17.06.2024 11:08
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
98
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы не хотите преобразовывать структуру данных, вы можете определить DataTemplateSelector, который определяет фактический тип элементов EntityDisplayObject.Collection и выбирает соответствующий DataTemplate:

class MyDataTemplateSelector : DataTemplateSelector
{
  public DataTemplate DefaultTemplate { get; set; }
  public DataTemplate NestedListTemplate { get; set; }

  public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    => item is IEnumerable 
      ? this.NestedListTemplate 
      : this.DefaultTemplate;
}
<Window xmlns:sys = "clr-namespace:System;assembly=mscorlib"
        xmlns:collections = "clr-namespace:System.Collections;assembly=System.Runtime">
  <TreeView x:Uid = "TreeView_2"
            Background = "Transparent"
            BorderThickness = "0"
            Padding = "0">

    <TreeViewItem x:Uid = "TreeViewItem_1"
                  Header = "{Binding CollectionName}">

      <ListBox x:Uid = "ListBox_2"
               ItemsSource = "{Binding Collection}"
               Background = "Transparent"
               BorderThickness = "0">
        <ListBox.ItemTemplateSelector>
          <local:MyDataTemplateSelector>
            <local:MyDataTemplateSelector.DefaultTemplate>
              <DataTemplate DataType = "{x:Type sys:Int32}">
                <TextBlock Text = "{Binding}" />
              </DataTemplate>
            </local:MyDataTemplateSelector.DefaultTemplate>

            <local:MyDataTemplateSelector.NestedListTemplate>
              <DataTemplate DataType = "{x:Type collections:IList}">
                <HeaderedItemsControl ItemsSource = "{Binding}" />
              </DataTemplate>
            </local:MyDataTemplateSelector.NestedListTemplate>
          </local:MyDataTemplateSelector>
        </ListBox.ItemTemplateSelector>
      </ListBox>
    </TreeViewItem>
  </TreeView>
</Window>

Однако вложенный список — плохой выбор для реализации древовидной структуры данных в объектно-ориентированном программировании. Это внесет некоторые странности.

Я рекомендую преобразовать вашу структуру данных, поскольку это значительно упростит работу с вашими данными и представлениями. Поэтому вместо List<List<int>> вы можете использовать List<EntityDisplayObject>. По соображениям производительности в примере List<T> заменяется на ObservableCollection<T>:

EntityDisplayObject.cs

class EntityDisplayObject : INotifyPropertyChanged
{
  public ObservableCOllection<EntityDisplayObject> Children { get; }

  public object Value { get; }

  public EntityDisplay(object value) : this(Enumerable.Empty<EntityDisplayObject>(), value)
  {
  }

  public EntityDisplay(IEnumerable<EntityDisplayObject> children, object value)
  {
    this.Children = new ObservableCollection<EntityDisplayObject>(children);
    this.Value = value;
  }
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public ObservableCollection<EntityDisplayObject> TreeData { get; } 

  public MainWindow()
  {
    InitializeComponent();

    this.Loaded += OnLoaded;
    this.TreeData = new ObservableCollection<EntityDisplayObject>();
  }

  private void OnLoaded(object sender, RoutedEventArgs e)
  {    
    var rootItem = new EntityDisplayObject("Root");

    // Create a child without nested children. 
    // Children are a simple list of int values.
    var intChildren = Enumerable.Range(1, 10);
    var simpleChild = new EntityDisplayObject(intChildren, "Simple child");
    rootItem.Children.Add(simpleChild);

    // Create a child with nested children.
    // Each child contains a list of int values
    IEnumerable<EntityDisplayObject> nestedChildren = Enumerable
      .Range(11, 10)
      .Select(value => 
        {
          var children = Enumerable.Range(100 * value, 10)
            .Select(childValue => new EntityDisplayObject(childValue));
          var child = new EntityDisplayObject(children, value);
        });
    var nestedChildrenParent = new EntityDisplayObject(nestedChildren, "Nested child #1");
    rootItem.Add(nestedChildrenParent);
  
    // Create another child with nested children.
    // Each child contains a list of int values
    nestedChildren = Enumerable
      .Range(21, 10)
      .Select(value => 
        {
          var children = Enumerable.Range(200 * value, 10)
            .Select(childValue => new EntityDisplayObject(childValue));
          var child = new EntityDisplayObject(children, value);
        });
    nestedChildrenParent = new EntityDisplayObject(nestedChildren, "Nested child #1");
    rootItem.Add(nestedChildrenParent);

    this.TreeData.Add(rootItem);
  }
}

MainWindow.xaml

<Window x:Name = "VisualRoot">
  <TreeView ItemsSource = {Binding ElementName=VisualRoot, Path=TreeData}">
    <TreeView.ItemTemplate>
      <HierarchicalDataTemplate DataType = "{x:Type EntityDisplayObject}" 
                                ItemsSource = "{Binding Children}">
        <TextBlock Text = "{Binding Value}" />
      </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
  </TreeView>
</Window>

Разве this.Children = children; не должно быть this.Children = new(children);?

Kostas K. 07.06.2024 12:50
For performance reasons the example replaces List<T> with ObservableCollection<T>: - есть ли у вас какая-либо документация, подтверждающая эту проблему с производительностью?
Rand Random 07.06.2024 12:51

@КостасК. Да, точно. Я написал этот код здесь, в текстовом редакторе, без проверки типа. Очевидно, я был немного неряшлив. Спасибо что подметил это.

BionicCode 07.06.2024 13:14

@RandRandom Нет ни одной статьи Microsoft. Это мои знания, основанные на нескольких статьях Microsoft и обзорах исходного кода. Пожалуйста, прочитайте: Привязка к списку приводит к утечке памяти для краткого обзора. Еще один момент, который не упоминается в этом сообщении, заключается в том, что использование реализации INotifyCollectionChanged позволяет обновлять экземпляр исходной коллекции вместо его замены для обновления представления, что позволяет избежать полного прохода рендеринга ItemsControl. что может быть очень дорого (обычно это приводит к зависанию пользовательского интерфейса).

BionicCode 07.06.2024 13:20
Ответ принят как подходящий

Спасибо всем за помощь. В конце концов, этот код выполнил свою работу. Что мне нужно было сделать, так это для каждой коллекции получить элементы и поместить их в новое свойство List, и если какие-либо элементы также являются коллекциями, сделать то же самое с ними, что даст каждому объекту список дочерних элементов (если они Коллекция).

foreach (var child in collection)
{
    if (child is ICollection childCollection && childCollection.Count > 0)
{
    var childObject = new EntityDisplayObject(childCollection);
    this.ChildCollection.Add(childObject);
}

А вот как выглядит xaml, позволяющий отображать что-то вроде этого

Item 1
  -Item 2
    -Item 3
    -Item 4
    -Item 5
      -Item 6
  -Item 7

<TreeView x:Uid = "TreeView_2"
          Background = "Transparent"
          BorderThickness = "0"
          Padding = "0"
          ScrollViewer.HorizontalScrollBarVisibility = "Disabled">

    <TreeViewItem x:Uid = "TreeViewItem_1"
                  Header = "{Binding CollectionName}">

        <ItemsControl ItemsSource = "{Binding ChildCollection}"
                      ItemTemplateSelector = "{StaticResource EntityInspectorDataTemplateSelector}"
                      Background = "Transparent"
                      BorderThickness = "0">
            
        </ItemsControl>

    </TreeViewItem>

</TreeView>

Другие вопросы по теме