Реализовать древовидную структуру WPF с разными родительскими узлами, а также с разными дочерними узлами? - PullRequest
5 голосов
/ 18 мая 2011

Я хочу реализовать древовидное представление со следующей структурой .....

[RootNode] <---- Корень дерева <br> - [ParentNode P1] <---- Объект ModelClass P1 <br> ---- [ChildNode C1] <----- Объект ModelClass C1 (также имеют детей другого типа) <br> ---- [ChildNode C2] <----- Объект ModelClass C2 (также есть дочерние объекты другого типа) <br> ---- [ChildNode C3] <----- Объект ModelClass C3 (также есть дочерние объекты другого типа) <br> - [ParentNode Q1] <---- Объект ModelClass Q1 <br> ---- [ChildNode B1] <----- Объект ModelClass B1 (также есть дочерние объекты другого типа) <br> ---- [ChildNode B2] <----- Объект ModelClass B2 (также имеют детей другого типа) <br> ---- [ChildNode B3] <----- Объект ModelClass B3 (также есть дочерние объекты другого типа) <br> - [ParentNode R1] <---- Объект ModelClass R1 <br> ---- [ChildNode A1] <----- Объект ModelClass A1 (также имеют детей другого типа) <br> ---- [ChildNode A2] <----- Объект ModelClass A2 (также имеют детей другого типа) <br> ---- [ChildNode A3] <----- Объект ModelClass A3 (также есть дочерние объекты другого типа) </p>

Я смотрел на многие решения, предложенные на этом сайте, а также в Интернете ..... но просто не могу понять, как это сделать .....

Это моя первая попытка использования Wpf, и это важнейшее требование ......

Также сложно создать объектную модель для разных классов .....

У всех классов, показанных выше, есть и другие свойства, включая их дочерние узлы ... я не хочу отображать все свойства только дочерние узлы

Полностью озадачен ... увидев другое решение

Было бы здорово, если бы я мог получить некоторую помощь в этом отношении ...

Спасибо

Ответы [ 3 ]

8 голосов
/ 10 июня 2011

Иерархические шаблоны данных работают, если вы используете пользовательские коллекции .... Я сделал мои классы такими:

public class EntityBase :ObservableCollection<object>
{

}

public class Parent : EntityBase
{

}  

public class ChildA : EntityBase // Dont make it a collection if it has noe childern to be displayed so dont inherit for EntityBase
{
   //Child Properties
}


public class ChildB : EntityBase
{
//Child Properties
}  

Теперь, когда вы, наконец, свяжете данные с TreeView, вы создадите ChildA иChildB элементы как дочерние элементы объекта Parent, т.е.

    public ObservableCollection<object> GetData()
    {
         var temp = new ObservableCollection<object>();
         Parent parent = new Parent(); // Root Node
         temp.Add(parent);
         parent.Add(new ChildA()); // ChildA as Child1 of Parent

         parent.Add(new ChildA()); // ChildA as Child2 of Parent

         parent.Add(new ChildB()); // ChildB as Child3 of Parent

         parent.Add(new ChildB()); // ChildB as Child4 of Parent

         return temp;

    }  

Наконец, шаблоны данных иерархии будут выглядеть следующим образом:

<TreeView Name="test" Grid.Row="0" ItemsSource="{Binding Path=TreeData,Source={StaticResource ResourceKey=DataSource}}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:Parent}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock>Parent</TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:ChildA}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock Text="{Binding Path = Name}"></TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
            <HierarchicalDataTemplate DataType="{x:Type EntityLayer:ChildB}" ItemsSource="{Binding}">
                <StackPanel>
                    <TextBlock Text="{Binding Path = Name}"></TextBlock>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>
2 голосов
/ 18 мая 2011

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

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

Настройка

Вот некоторые из основ, которые я сделалради воссоздания аналогичной ситуации.Каждый из различных классов наследуется от List<object>, так что он имеет встроенный способ содержать дочерние элементы любого типа, но я не зависим от этой общности при выборе шаблонов данных.

public class P1 : List<object> {
    public P1() {}
    public P1( IEnumerable<object> collection ) : base( collection ) {}
}

Кроме тогомой корневой источник данных имеет тип List<object>, поэтому он может содержать любые типы объектов.

Конструктор Window:

public MainWindow() {
    InitializeComponent();
    this.DataContext = MyDataSource.GetData(); // in which I construct the tree of parents and children
}

Solution

Начните с HierarchicalDataTemplate s для каждого типа.Если какой-либо из типов не содержит дочерние элементы, вы, конечно, вместо них сделаете для них DataTemplate s:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C1}"
                          ItemsSource="{Binding}">
    <TextBlock>a C1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:C2}"
                          ItemsSource="{Binding}">
    <TextBlock>a C2 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:Q1}"
                          ItemsSource="{Binding}">
    <TextBlock>a Q1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B1}"
                          ItemsSource="{Binding}">
    <TextBlock>a B1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:B2}"
                          ItemsSource="{Binding}">
    <TextBlock>a B2 object</TextBlock>
</HierarchicalDataTemplate>

Поскольку каждый класс происходит от List<object>, сам объект является источником дочерних элементов.предметы, а не коллекция на имущество, поэтому я использую ItemsSource="{Binding}".Если дочерние элементы находятся в коллекции в свойстве, называемом Children, оно, конечно, будет ItemsSource="{Binding Children}".Это по-прежнему позволяет каждому объекту иметь своих детей в другом месте.

Самый простой способ реализовать DataTemplateSelector в этом примере - ничего не делать.Поскольку в шаблонах данных я указал только DataType, а не x:Key, хотя коллекции нечеткие (List<object>), WPF все равно будет проверять базовый тип, чтобы определить, является ли он P1 / Q1 /и т.п.и найдите правильный HierarchicalDataTemplate для использования.Мой TreeView должен выглядеть только так:

<TreeView ItemsSource"{Binding}" />

Однако, скажем ради того, чтобы показать вам DataTemplateSelector, что вы не можете полагаться на его неявное соответствие с помощью Type.Вы поместили бы x:Key s на ваши шаблоны данных, например:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>

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

public class CustomDataTemplateSelector : DataTemplateSelector {
    static Dictionary<Type, object> typeToKey = new Dictionary<Type, object>();
    static CustomDataTemplateSelector() {
        typeToKey[ typeof( P1 ) ] = "myKeyforP1";
    }

    public override DataTemplate SelectTemplate( object item, DependencyObject container ) {
        var element = container as FrameworkElement;
        if ( element != null && item != null ) {
            var itemtype = item.GetType();
            object keyObject;
            if ( typeToKey.TryGetValue( itemtype, out keyObject ) ) {
                var template = element.TryFindResource( keyObject ) as DataTemplate;
                if ( template != null ) {
                    return template;
                }
            }
        }
        return base.SelectTemplate( item, container );
    }
}

Затем вы добавите селектор в словарь ресурсов, и вашему TreeView потребуется другое назначенное свойство:

<Grid.Resources>
    <loc:CustomDataTemplateSelector x:Key="mySelector" />
</Grid.Resources>
<TreeView ItemsSource="{Binding}"
          ItemTemplateSelector="{StaticResource mySelector}"></TreeView>

И поскольку base.SelectTemplate() будет пытаться использовать Type элемента в качестве ключа ресурса, вы можете иметь стандартный HierarchicalDataTemplate для модели и настроенную версию, которая будет использоваться только в том случае, если ваш TreeView использует пользовательский DataTemplateSelector:

<HierarchicalDataTemplate DataType="{x:Type loc:P1}"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 object</TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:P1}" x:Key="myKeyforP1"
                          ItemsSource="{Binding}">
    <TextBlock>a P1 that looks much different</TextBlock>
</HierarchicalDataTemplate>
0 голосов
/ 08 июня 2011

Возможно ли вы можете опубликовать более подробную информацию о вашем решении.Я пытаюсь добиться того же, я добавил несколько иерархических шаблонов данных, но мне было бы интересно посмотреть объектную модель.

В моем случае у меня есть родительский элемент со следующими свойствами

Public string Name { get; set; }
Public ObservableCollection<ChildA> ChildrenA { get; set; }
Public ObservableCollection<ChildB> ChildrenB { get; set; }

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

...