【问题标题】:Implement WPF treeview with different Parent Nodes a well as different child nodes?用不同的父节点和不同的子节点实现 WPF 树视图?
【发布时间】:2011-05-18 13:44:54
【问题描述】:

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

[RootNode] --[ParentNode P1] ----[ChildNode C1] ----[ChildNode C2] ----[ChildNode C3] --[ParentNode Q1] ----[ChildNode B1] ----[ChildNode B2] ----[ChildNode B3] --[ParentNode R1] ----[ChildNode A1] ----[ChildNode A2] ----[ChildNode A3]

我已经查看了本网站以及网络上提出的许多解决方案.....但就是不知道该怎么做.....

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

还发现很难为上述不同类制作对象模型.....

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

完全被...看到不同的解决方案感到困惑

如果我能在这方面得到一些帮助,那就太好了……

谢谢

【问题讨论】:

    标签: wpf treeview lazy-loading hierarchicaldatatemplate


    【解决方案1】:

    如果您使用自定义集合,分层数据模板可以工作...... 我的课程是这样的:

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

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

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

      如果这些类中的任何一个具有共同的基类,那么您会更容易,例如,您可以将一个DataTemplate 用于多个类。

      但是,如果这些模型中的每一个确实不同并且没有足够的共同点,则您需要使用DataTemplateSelector,尽管内置机制可能就足够了。

      设置

      这是我为重现类似情况而打下的一些基础。每个不同的类都继承自List&lt;object&gt;,因此它具有包含任何类型子级的内置方式,但我不依赖于选择数据模板的通用性。

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

      此外,我的根数据源是List&lt;object&gt; 类型,因此它可以包含任何类型的对象。

      Window 的构造函数:

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

      解决方案

      首先为每种类型创建HierarchicalDataTemplates。如果任何类型不包含子项,您当然会为它们创建DataTemplates:

      <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&lt;object&gt;,因此对象本身是子项的来源,而不是属性上的集合,因此我使用ItemsSource="{Binding}"。如果子项位于名为Children 的属性下的集合中,那么它当然是ItemsSource="{Binding Children}"。这仍然允许每个对象在不同的​​位置拥有其子对象。

      在此示例中实现 DataTemplateSelector 的最简单方法是什么都不做。因为我只在数据模板上指定了DataType 而不是x:Key,即使集合是模糊的(List&lt;object&gt;) WPF 仍然会检查底层类型以确定它是否是P1/Q1/等等并找到正确的HierarchicalDataTemplate 使用。我的TreeView 只需看起来像这样:

      <TreeView ItemsSource"{Binding}" />
      

      但是,假设为了向您展示 DataTemplateSelector,您不能依赖它与 Type 的隐式匹配。您可以将x:Keys 放在您的数据模板中,例如:

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

      【讨论】:

      • 感谢您的帮助...... . 继续上面的 senario 我必须为 Root 节点创建一个类吗???我制作的树没有根节点,它的父节点开始......这个结构可以序列化和反序列化
      • WPF 会通过自动方式尝试查找正确的DataTemplate,主要基于模板的DataType 与它尝试模板化的对象相同。 DataTemplateSelectors 适用于当您需要一种方法来自定义选择什么模板来显示特定类型时,尤其是当您为给定类型有多个可能的模板或想要随时切换它们时。你甚至可以在选择器中创建模板并返回它。
      • 要看根节点有没有自己的属性或者只是曾经是P1、Q1、R1等的父节点,如果是后一种情况,意义不大制作一个单独的课程。我所做的只是用顶级父节点创建一个List 并将其用作数据源。至于序列化,这是一个不同的问题。通常你用属性来标记你的类,这些属性指导序列化器如何序列化它们。
      • 谢谢,我将继续遵循以下准则
      【解决方案3】:

      您是否可以发布解决方案的更多详细信息。我正在尝试实现相同的目标,我添加了多个分层数据模板,但有兴趣查看对象模型。

      就我而言,我有一个具有以下属性的父项

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

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

      【讨论】:

      • 我在这里发布了我的解决方案,您可以查看...如果您发现建议一些改进..这会很有帮助..
      • 这就是你要找的东西。
      猜你喜欢
      • 2015-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多