什么是带有 CollectionViewGroup 分组的惯用 XAML TreeView?

What's an idiomatic XAML TreeView with CollectionViewGroup groupings?

我有一个 TreeView 看起来像这样:

<TreeView Grid.Row="1" x:Name="InspectionResultsTreeView"
          ItemsSource="{Binding Source={StaticResource InspectionTypeGroupViewSource}, Path=Groups}"
          ItemTemplate="{StaticResource InspectionTypeGroupsTemplate}">
</TreeView>

ItemsSource 是一个键控资源,名称为 InspectionTypeGroupViewSource:

<CollectionViewSource x:Key="InspectionTypeGroupViewSource" Source="{Binding Results}">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Inspection.InspectionType" />
        <PropertyGroupDescription PropertyName="Inspection" />
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

这个小东西的作用是取ViewModel的Results 属性:

private ObservableCollection<ICodeInspectionResult> _results;

public ObservableCollection<ICodeInspectionResult> Results
{
    get { return _results; } 
    set { _results = value; OnPropertyChanged(); }
}

...并将其分组为两个级别 - 首先按 InspectionType,然后按 Inspection - 结果是一个 3 级层次结构,其中包含检查类型、检查,然后是单个检查结果.在这一点上,屏幕截图可能有助于可视化我猜:

因此,InspectionResultsTreeViewItemTemplate 是另一个键控资源,名称为 InspectionTypeGroupsTemplate - 即 bold "inspection type" 项:

<HierarchicalDataTemplate x:Key="InspectionTypeGroupsTemplate" 
                            DataType="{x:Type CollectionViewGroup}"
                            ItemsSource="{Binding Items}"
                            ItemTemplate="{StaticResource InspectionGroupsTemplate}">

    <StackPanel Orientation="Horizontal">
        <TextBlock VerticalAlignment="Center" 
                   Text="{Binding Name}"
                   FontWeight="Bold"
                   TextWrapping="NoWrap"/>
        <TextBlock Margin="4,0,4,0" 
                   VerticalAlignment="Center" 
                   Text="{Binding ItemCount, StringFormat=({0})}" 
                   TextWrapping="NoWrap"/>
    </StackPanel>
</HierarchicalDataTemplate>

并且该模板的 ItemTemplate 是一个 InspectionGroupsTemplate - 那是单独的检查,带有 "severity" 图标:

<HierarchicalDataTemplate x:Key="InspectionGroupsTemplate"
                          DataType="{x:Type CollectionViewGroup}"
                          ItemsSource="{Binding Items}"
                          ItemTemplate="{StaticResource InspectionResultTemplate}">

    <StackPanel Orientation="Horizontal">
        <Image Style="{StaticResource IconStyle}" 
               Source="{Binding Name, Converter={StaticResource InspectionIconConverter}}"
               VerticalAlignment="Center" />
        <TextBlock Margin="4" 
                   VerticalAlignment="Center" 
                   Text="{Binding Name, Converter={StaticResource InspectionDescriptionConverter}}"
                   TextWrapping="NoWrap"/>
        <TextBlock Margin="0,4,0,4" 
                   VerticalAlignment="Center" 
                   Text="{Binding ItemCount, StringFormat=({0})}" 
                   TextWrapping="NoWrap"/>
    </StackPanel>
</HierarchicalDataTemplate>

最后,这个分组的ItemTemplate是一个InspectionResultTemplate,对于每个单独的检查结果:

<DataTemplate x:Key="InspectionResultTemplate" 
              DataType="{x:Type inspections:ICodeInspectionResult}">
    <StackPanel Orientation="Horizontal">
        <TextBlock VerticalAlignment="Center" 
                   Margin="4"
                   Text="{Binding Name}" 
                   TextWrapping="NoWrap"/>
    </StackPanel>
</DataTemplate>

ICodeInspectionResult 接口有一个 string Name 属性,我在这里使用;这个 Name 与分组级别中使用的 Name 不同,后者是 object CollectionViewGroup.Name - Name 的基础类型是分组的类型,因此级别 1是一个InspectionType,2级是一个Inspection

问题是我使用的转换器比我认为需要的要多,以转换此 object Name 并访问我需要访问和显示的成员...但是,我需要显示每个分组中的项目数,因此 DataType 应该是 CollectionViewGroup... 对吗?

如何在不借助转换器来显示所有需要显示的内容的情况下执行此操作? 应该如何完成?我能找到的每个 TreeView / CollectionViewGroup 教程都是一个微不足道的实现。

您遇到了 XAML 的标志性问题:它几乎 结构化了。

惯用的解决方案是编写自定义 WPF 用户控件。 (如何以及在其中包含什么由您决定。)WPF 用户控件的目标是消除重复的 XAML 标记和逻辑。您可以将 Converter 包含在用户控件中,并从主控件中删除转换器。

在WPF中创建UserControl对象的教程很多,这里就不赘述了。


至于 Converter 问题:这是 几乎 最惯用的方式。每个转换器都是可重复使用的,并且只专注于一种源类型。除了考虑将支持相同源类型的转换器合并在一起之外,您无能为力。 (转换器有一个 Type targetType 参数和一个 object parameter 是有原因的。)