wpf 从DataTemplate内部使用具有HierarchicalDataTemplate的TreeView层次结构

1cklez4t  于 2023-10-22  发布在  其他
关注(0)|答案(1)|浏览(150)

我有一个使用TreeView的WPF应用程序,在TreeView内部有多个HierarchicalDataTemplates/DataTemplates用于不同类型,每个包含一个ContentControl和一个特定的Template,如下所示:

TreeView
|- HierarchicalDataTemplate for Type a
|  |- ContentControl
|
|- DataTemplate for Type b
   |- ContentControl

b类型是这样构建的:

b
|-integer c
|-object d

d可以是从整数到字符串的任何值,但也可以是包含列表的类。在这种情况下,我想使用上面描述的TreeView中的HierarchicalDataTemplate显示d的列表。
有没有办法做到这一点,或者我一进入DataTemplate/ContentControl/Template就失去了与TreeView层次结构的所有连接?

8ehkhllq

8ehkhllq1#

对于像这样的复杂场景,您可以实现自定义的DataTemplateSelector。根据您的描述,我假设AB的数据类型如下,并具有CD的属性:

public class A
{
}

public class B
{
   public B(int c, object d)
   {
      C = c;
      D = d;
   }

   public int C { get; }

   public object D { get; }
}

您可以为每种类型和用途创建自定义数据模板。对于B,当D是一个集合时,将有一个DataTemplate用于常规类型,一个HierarchicalDataTemplate用于:

<TreeView ItemsSource="{Binding MyItems}">
   <TreeView.ItemTemplateSelector>
      <local:CustomDataTemplateSelector/>
   </TreeView.ItemTemplateSelector>
   <TreeView.Resources>
      <DataTemplate x:Key="ATemplate"
                    DataType="{x:Type local:A}">
         <TextBlock Text="This is an A."/>
      </DataTemplate>
      <DataTemplate x:Key="BTemplate" DataType="{x:Type local:B}">
         <StackPanel>
            <TextBlock Text="{Binding C}"/>
            <TextBlock Text="{Binding D}"/>
         </StackPanel>
      </DataTemplate>
      <HierarchicalDataTemplate x:Key="BHierarchicalTemplate"
                                DataType="{x:Type local:B}"
                                ItemsSource="{Binding D}">
         <TextBlock Text="{Binding C}"/>
      </HierarchicalDataTemplate>
   </TreeView.Resources>
</TreeView>

需要x:Key来使用DataTemplateSelector解析数据模板。在这种情况下,我们将检查一个项目是否是A类型,并使用ATemplate。如果它是B,我们通过检查属性D来检查它的模板是否需要分层。如果它是一个集合,或者更一般地说是IEnumerable,我们使用分层模板。但是,要注意,有些类型(如string)也是可编译的,因此我们需要进行单独的检查。

public class CustomDataTemplateSelector : DataTemplateSelector
{
   private const string ATemplateName = "ATemplate";
   private const string BTemplateName = "BTemplate";
   private const string BHierarchicalTemplateName = "BHierarchicalTemplate";

   public override DataTemplate SelectTemplate(object item, DependencyObject container)
   {
      if (!(container is FrameworkElement frameworkElement))
         return base.SelectTemplate(item, container);

      // >= C# 6
      //switch (item)
      //{
      //   case A:
      //      return FindDataTemplate(frameworkElement, ATemplateName);
      //   case B b when b.D is string:
      //      return FindDataTemplate(frameworkElement, BTemplateName);
      //   case B b when b.D is IEnumerable:
      //      return FindDataTemplate(frameworkElement, BHierarchicalTemplateName);
      //   case B:
      //      return FindDataTemplate(frameworkElement, BTemplateName);
      //   default:
      //      return base.SelectTemplate(item, container);
      //}
      
      // >= C# 8
      return item switch
      {
         A => FindDataTemplate(frameworkElement, ATemplateName),
         B { D: string } => FindDataTemplate(frameworkElement, BTemplateName),
         B { D: IEnumerable } => FindDataTemplate(frameworkElement, BHierarchicalTemplateName),
         B => FindDataTemplate(frameworkElement, BTemplateName),
         _ => base.SelectTemplate(item, container)
      };
   }

      private static DataTemplate FindDataTemplate(FrameworkElement frameworkElement, string key)
   {
      return (DataTemplate)frameworkElement.FindResource(key);
   }
}

是为模板名称创建常量,还是从类型名称构建键,还是公开属性来分配数据模板,这取决于您的要求。

相关问题