С# WPF достигает объекта в ObservableCollection, связывая TreeViewItem

Я работаю над проектом WPF, пытаясь получить информацию от объекта ImpactElement. Этот объект находится внутри ObservableCollection с именем ElementList. Этот ElementList находится внутри другой ObservableCollection с именем ListOfCreatedStacks, которая содержит объекты стека классов.

Он показывает только toString из элемента (ImpactElement) внутри ElementList. Я хочу, чтобы переменная ImpactElements ElementMark отображалась для каждого элемента в ElementList.

Вот как это выглядит сейчас Один элемент добавлен в ElementList

Я могу создать любое количество стеков, и внутри каждого стека есть ElementList с разным количеством ImpactElement. Два стека с разным количеством ImpactElements в ElementList

Из этих изображений вы можете видеть, что каждый элемент ImpactElement из ElementList отображается как «IMPACT_Visual_Stacker.Model.ImpactElement», но я хочу, чтобы это была переменная ElementMark, которая находится внутри класса ImpactElement.

Вот код из разных классов.

public class Controller
{
    public ObservableCollection<Stack> ListOfCreatedStacks { get { return listOfCreatedStacks; } set { listOfCreatedStacks = value; } }
}

public class Stack
{
    public ObservableCollection<ImpactElement> ElementList { get { return elementList; } set { elementList = value; } }
    public string Id { get { return id; } set { id = value; } }
}

public class ImpactElement
{
    private string elementMark;
    private int id;
    private Vector3 sizeLWH;
    private Vector3 positionXYZ;
    private Vector3 rotationXYZ;
    private Mesh elementMesh;
}

Вот часть XAML.

 <ListView ItemsSource="{Binding ListOfCreatedStacks}" HorizontalAlignment="Left" Margin="350, 200,0,0" Width="300">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TreeViewItem
                    Header="{Binding Id}"
                    IsExpanded="True">
                    <TreeViewItem
                        ItemsSource="{Binding ElementList}"  Header="{Binding ElementMark}" IsExpanded="True">
                    </TreeViewItem>
                </TreeViewItem>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

Я попробовал решение из другого StackOverflow, которое я получил от Эда. В результате добавляется только объект Hiearchy вместо моих объектов "ImpactElement".

Вот как выглядит проверенный XAML:

 <ListView ItemsSource="{Binding ListOfCreatedStacks}" HorizontalAlignment="Left" Margin="350, 200,0,0" Width="300">
        <ListView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type System:String}"
                                      ItemsSource="{Binding ListOfCreatedStacks}">
                <TreeViewItem
                    Header="{Binding Id}"
                    IsExpanded="True">
                    <HierarchicalDataTemplate DataType="{x:Type System:String}"
                                              ItemsSource="{Binding ElementList}">
                        <TreeViewItem
                            Header="{Binding ElementMark}"
                            IsExpanded="True">
                        </TreeViewItem>
                    </HierarchicalDataTemplate>
                </TreeViewItem>
            </HierarchicalDataTemplate>

        </ListView.ItemTemplate>

    </ListView>

Такое ощущение, что я пропустил какую-то простую часть, которую я не понимаю. Спасибо за помощь.


person newToCSharp    schedule 16.05.2017    source источник
comment
stackoverflow.com/questions/19097338/   -  person 15ee8f99-57ff-4f92-890c-b56153    schedule 16.05.2017
comment
Спасибо за ответ Эд. Это был хороший ответ на этот вопрос. Поскольку у меня не тот же ребенок и родители, а не Иерахия, я не вижу, как я могу это применить?   -  person newToCSharp    schedule 16.05.2017
comment
Используйте HierarchicalDataTemplate. Если вы не можете изменить этот ответ в соответствии со своими потребностями, задайте другой вопрос о месте, где вы столкнулись с проблемой. Если вы не хотите попробовать, извините, но я не могу вам помочь.   -  person 15ee8f99-57ff-4f92-890c-b56153    schedule 16.05.2017
comment
Привет Эд. Я попробовал решение из присланного вами ответа. Я не могу заставить его работать. Я обновил исходный вопрос. Вы видите, что я скучаю?   -  person newToCSharp    schedule 17.05.2017
comment
Почему вы пытаетесь вложить TreeViewItem в ListView вместо TreeView?   -  person grek40    schedule 17.05.2017
comment
grek40 это хороший вопрос. Я действительно не знаю. Единственное, что мне нужно сделать, это ListView. Может ли быть проблема в ListView?   -  person newToCSharp    schedule 17.05.2017
comment
Я попытался изменить ListView на TreeView, это сработало, но все та же проблема. Я не дохожу до ElementList. Как будто привязка останавливается на ListOfCreatedStacks   -  person newToCSharp    schedule 17.05.2017
comment
@newToCSharp Теперь мы в деле! Я взгляну.   -  person 15ee8f99-57ff-4f92-890c-b56153    schedule 17.05.2017


Ответы (1)


Во-первых, Controller, ElementList и ImpactElement, вероятно, должны реализовывать INotifyPropertyChanged. Вы можете найти миллион таких примеров на Stack Overflow и в других местах. Это просто, и вы можете получить здесь помощь, если столкнетесь с какими-либо препятствиями.

Это приведет к обновлению пользовательского интерфейса, если вы измените какие-либо значения связанных свойств для этих объектов, пока они видны в пользовательском интерфейсе. ObservableCollections уведомит пользовательский интерфейс, если вы добавите или удалите элементы, но они не могут уведомить пользовательский интерфейс, если какое-либо свойство одного из элементов изменится.

Во-вторых, ImpactElement имеет только приватные поля. Поскольку приватные поля были опущены в других классах, я предполагаю, что это была просто оплошность, когда вы копировали код. Но вы можете привязываться только к общедоступным свойствам. Должен быть общедоступным, должен быть свойством с get и обычно с set.

Я надеюсь, что вы назначили экземпляр Controller вашему DataContext. Если вы этого не сделаете, дерево не будет заполнено.

Сначала я обнаружил, что TreeView WPF сбивает с толку, и я не был новичком в XAML, когда впервые посмотрел на него. Сделайте кучу простых примеров (и обращайтесь сюда за помощью, если она вам понадобится), а не просто вставляйте XAML, который я вам дал. Он попадет в фокус.

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

Итак, вот XAML:

<TreeView 
    ItemsSource="{Binding ListOfCreatedStacks}" 
    HorizontalAlignment="Left" 
    Margin="350, 200,0,0"
    Width="300">
    <TreeView.ItemContainerStyle>
        <Style 
            TargetType="TreeViewItem" 
            BasedOn="{StaticResource {x:Type TreeViewItem}}"
            >
            <Setter Property="IsExpanded" Value="True" />
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate 
            DataType="{x:Type local:Stack}"
            ItemsSource="{Binding ElementList}"
            >
            <Label Content="{Binding Id}" />
            <HierarchicalDataTemplate.ItemTemplate>
                <HierarchicalDataTemplate 
                    DataType="{x:Type local:ImpactElement}"
                    >
                    <Label Content="{Binding ElementMark}" />
                </HierarchicalDataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Таким образом, ItemsSource TreeView равен ListOfCreatedStacks.

Мы даем TreeView значение ItemContainerStyle, которое будет применяться к каждому TreeViewItem в дереве. Мы делаем это только для того, чтобы установить IsExpanded. Другого способа добраться до TreeViewItem нет. Мы могли также дать шаблону элемента верхнего уровня собственный ItemContainerStyle, который повлияет только на его дочерние элементы, если мы хотим сделать с ними что-то другое. Но нам это не нужно.

И мы даем TreeView ItemTemplate. Вот как мы показываем TreeViewItem, как отображать одного из дочерних элементов TreeView. Когда вы смотрите на HierarchicalDataTemplate, это немного похоже на TreeView: у него есть ItemsSource, который мы связываем со свойством Stack, которое содержит дочерние элементы Stack. У него есть ItemTemplate для собственных детей. Как я уже сказал, он может иметь ItemContainerStyle, но мы просто позволим ему наследовать тот, который мы установили для TreeView.

Это рекурсивно: узел дерева очень похож на целое дерево.

Если вы не дадите HierarchicalDataTemplate собственное ItemTemplate, TreeViewItems, к которому оно применяется, просто будет использовать его в качестве шаблона для своих дочерних элементов, и дочерние элементы будут делать то же самое. Если вы дали ImpactElement дочернюю коллекцию ImpactElement, дерево может иметь два десятка уровней в глубину без внесения единого изменения в приведенный выше XAML.

Любое DataTemplate или HierarchicalDataTempate может иметь свойство DataType. Это тип элемента модели представления, отображаемый шаблоном. Я не знаю, откуда у вас System:String.

В данном конкретном случае1 DataType не служит никакой цели, кроме как помочь вам не сбиваться с пути при редактировании XAML: он напоминает вам, какой шаблон и на каком уровне рекурсии применяется к какому элементу коллекции, и он Intellisense также поможет вам.

Наконец, у нас есть содержимое HierarchicalDataTempate:

<Label Content="{Binding Id}" />

Это может быть любой отдельный элемент XAML, но это может быть Grid или StackPanel, содержащий весь пользовательский интерфейс, если хотите. Туда можно положить что угодно. Вы могли бы поставить совершенно другой TreeView туда.

Наконец, вы должны научиться делать макет с StackPanel и Grid столбцами и строками. Этот маржинальный бизнес — кошмар, если вы хотите что-то изменить. Я предполагаю, что вы используете режим дизайна в VS? Все, что я использую, это предварительный просмотр того, что я сделал вручную в XAML. При правильном выполнении в макете XAML все позиционируется относительно своего родителя и братьев и сестер. Может быть, он имеет фиксированную ширину и/или высоту, обычно небольшой отступ — одиночные цифры — просто для создания пространства между соседями, а не для абсолютного позиционирования. Как только вы привыкнете к этому, это действительно хороший способ работать с вещами. Очень похоже на HTML.


1 Другие способы использования свойства DataType для DataTemplate или HierarchicalDataTemplate см. в разделе "неявные шаблоны данных", а вот более сложный пример того, как сделать это с элементами дерева. Если вы уже изо всех сил пытаетесь переварить все это, не смотрите на эти вещи, просто сосредоточьтесь на случае, который я дал вам выше. Этого более чем достаточно. Шаблоны данных — это кривая обучения, а добавление рекурсии только делает все более странным.

person 15ee8f99-57ff-4f92-890c-b56153    schedule 17.05.2017