使用不同的父节点和不同的子节点实现WPF树视图?

时间:2011-05-18 13:44:55

标签: wpf treeview lazy-loading hierarchicaldatatemplate

我想实现一个具有以下结构的树视图.....

[RootNode]< ----树的根
     - [ParentNode P1]< ---- ModelClass P1的对象
        ---- [ChildNode C1]< ----- ModelClass C1的对象(也有不同类型的子节点)
        ---- [ChildNode C2]< ----- ModelClass C2的对象(也有不同类型的子节点)
        ---- [ChildNode C3]< ----- ModelClass C3的对象(也有不同类型的子节点)
     - [ParentNode Q1]< ---- ModelClass Q1的对象         ---- [ChildNode B1]< ----- ModelClass B1的对象(也有不同类型的子节点)
        ---- [ChildNode B2]< ----- ModelClass B2的对象(也有不同类型的孩子)
        ---- [ChildNode B3]< ----- ModelClass B3的对象(也有不同类型的孩子)
     - [ParentNode R1]< ---- ModelClass R1的对象
        ---- [ChildNode A1]< ----- ModelClass A1的对象(也有不同类型的子节点)
        ---- [ChildNode A2]< ----- ModelClass A2的对象(也有不同类型的子节点)
        ---- [ChildNode A3]< ----- ModelClass A3的对象(也有不同类型的子节点)

我已经查看过本网站以及网站上提出的许多解决方案.....但只是无法弄清楚如何做到这一点......

这是我对Wpf的第一次尝试,这是一个至关重要的要求......

也很难找到上述不同类的对象模型.....

上面显示的所有类都有其他属性,包括它们的子节点...... 我不想只显示子节点的所有属性

总是感到困惑......看到不同的解决方案

如果我能在这方面得到一些帮助,真的很棒......

谢谢

3 个答案:

答案 0 :(得分:8)

如果您使用自定义集合,则分层数据模板有效.... 我的课程是这样的:

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时,您将ChildAChildB项目设为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;

    }  

最后,Hierarchial数据模板看起来像..

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

答案 1 :(得分:2)

如果这些类中的任何一个具有相同的基类,那么对您来说会更容易,例如,您可以为多个类使用一个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
}

解决方案

首先为每种类型设置HierarchicalDataTemplate。如果任何类型不包含子项,那么您当然会为它们制作DataTemplate

<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仍会检查基础类型以确定它是否为{{{ 1}} / P1 /等。并找到要使用的正确Q1。我的HierarchicalDataTemplate只需看起来像这样:

TreeView

但是,为了向您显示<TreeView ItemsSource"{Binding}" /> ,您不能依赖DataTemplateSelector隐式匹配它。您将Type放在datatemplate上,例如:

x:Key

然后,您的选择器可能会在内部使用<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

答案 2 :(得分:0)

您是否可以发布解决方案的更多详细信息。我正在努力实现同样的目标,我已经添加了多个hierarchicaldatatemplates但是有兴趣看到对象模型。

在我的情况下,我有一个具有以下属性的父

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

我想在树视图中显示这些内容。有兴趣了解我如何构建对象模型,因为我需要在单个ChildA和ChildB集合上方的树中显示某些内容。