我有一个可观察到的嵌套对象集合,我在WPF中以TreeView显示这些对象.

public class NestedContainer
{
    public FXR1.FXContainer myContainer { get; set; }
    public string XID { get { return myContainer.XID; } }
    public FXR1.FXActionData ActionData { get { return myContainer.ActionData; } }
    public FXR1.FXModifier Modifier { get { return myContainer.Modifier; } }
    public List<NestedContainer> childContainers { get; set; } = new List<NestedContainer>();
}

我成功地创建了我想要的嵌套 struct ,使用了下面的XAML代码,它显示在树视图中,如下所示:

<TreeView x:Name="FXNodeTree" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:NestedContainer}" ItemsSource="{Binding Path=childContainers}">
            <TextBlock Text="{Binding Path=XID}" />
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

Current display example

但是,我也try 将与每个容器相关联的ActionDataModifier(如果有的话)显示为TreeView中位于容器childContainers的正下方的项.这些项没有自己的子容器,因此它们只是属于每个容器的附加信息.经过一些研究后,我最初的 idea 是在childContainers的基础上使用多重绑定和绑定ActionDataModifier,但我找不到任何类似的例子,所以我不确定这是否是理想的解决方案.我对WPF的了解非常初级,所以如果这是一个非常基本的问题,或者如果我错过了一个明显的解决方案,我表示歉意.如有任何帮助,我们不胜感激.

推荐答案

如果我理解正确的话,您希望显示每个 node 数据的附加信息.

For this purpose, you should override the default ControlTeplate of the TreeViewItem. This way you can inject a second content host below the items host (ItemsPresenter).
If you introduce an attached property to hold a DataTemplate that describes the additional content you can avoid to subclass TreeViewItem. This attached property can be defined on any suitable type. This example chooses the MainWindow.

为了避免内存泄漏,绑定源必须始终实现INotifyPropertyChanged,或者最好将属性实现为依赖项属性.即使财产价值不会改变.

The following example shows a fragment of the extracted TreeViewItem default Style. It misses other referenced resources in order to compact the example.
You can extract the complete Style using the XAML designer and then paste the Style for the TreeViewItem from below to replace the extracted Style of the TreeViewItem.
I have annotated the modified parts to help spotting the required modifications.

MainWindow.xanl.cs

partial class MainWindow : Window
{
  public static DataTemplate GetExtendedContentItemTemplate(DependencyObject attachingElement) 
    => (DataTemplate)attachingElement.GetValue(ExtendedContentItemTemplateProperty);

  public static void SetExtendedContentItemTemplate(DependencyObject attachingElement, DataTemplate value) 
    => attachingElement.SetValue(ExtendedContentItemTemplateProperty, value);

  public static readonly DependencyProperty ExtendedContentItemTemplateProperty = DependencyProperty.RegisterAttached(
    "ExtendedContentItemTemplate", 
    typeof(DataTemplate), 
    typeof(MainWindow), 
    new PropertyMetadata(default));
}

MainWindow.xaml

<Window>
  <Window.Resources>
    
    <!-- The example DataTemplate for the extended content -->
    <DataTemplate x:Key="ExtendedDataContentTemplate"
                  DataType="NestedContainer">
      <StackPanel>
        <TextBlock Text="{Binding ActionData}" />
        <TextBlock Text="{Binding Modifier}" />
      </StackPanel>
    </DataTemplate>

    <!-- 
         The fragment of the complete TreeViewItem style.
         The first setter that sets the attached 'ExtendedContentItemTemplate' property
         and the ControlTemplate that extends the layout to add the ContentControl 
         for the additional content are of special interest here.
    -->
    <Style TargetType="{x:Type TreeViewItem}">

      <!-- Assign the DataTemplate for the extended content to the attached property -->
      <Setter Property="local:MainWindow.ExtendedContentItemTemplate"
              Value="{StaticResource ExtendedDataContentTemplate}" />

      <Setter Property="Background"
              Value="Transparent" />
      <Setter Property="HorizontalContentAlignment"
              Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
      <Setter Property="VerticalContentAlignment"
              Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
      <Setter Property="Padding"
              Value="1,0,0,0" />
      <Setter Property="Foreground"
              Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
      <Setter Property="FocusVisualStyle"
              Value="{StaticResource TreeViewItemFocusVisual}" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type TreeViewItem}">
            <Grid>
              <Grid.ColumnDefinitions>
                <ColumnDefinition MinWidth="19"
                                  Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
              </Grid.ColumnDefinitions>
              <Grid.RowDefinitions>
                <RowDefinition Height="Auto" /> <!-- Default header row -->
                <RowDefinition /> <!-- Default child items row -->
                <RowDefinition Height="Auto" /> <!-- New extra content row -->
              </Grid.RowDefinitions>
              <ToggleButton x:Name="Expander"
                            ClickMode="Press"
                            IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                            Style="{StaticResource ExpandCollapseToggleStyle}" />
              <Border x:Name="Bd"
                      Background="{TemplateBinding Background}"
                      BorderBrush="{TemplateBinding BorderBrush}"
                      BorderThickness="{TemplateBinding BorderThickness}"
                      Grid.Column="1"
                      Padding="{TemplateBinding Padding}"
                      SnapsToDevicePixels="true">
                <ContentPresenter x:Name="PART_Header"
                                  ContentSource="Header"
                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
              </Border>
              <ItemsPresenter x:Name="ItemsHost"
                              Grid.Column="1"
                              Grid.ColumnSpan="2"
                              Grid.Row="1" />
    
              <!-- 
                   The host for the extra content below the child items.
                   For this purpose we have to introduce a third row to the Grid.
                   The Content is the data item itself (in this case 'NestedContainer').
                   The ContentTemplate binds to the attached property ExtendedContentItemTemplate 
                   that is set on the TreeViewItem via the current Style (see style setter above).
              --> 
              <ContentControl Grid.Row="2"
                              Grid.Column="1"
                              Content="{TemplateBinding DataContext}"
                              ContentTemplate="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:MainWindow.ExtendedContentItemTemplate)}" />
            </Grid>

            <ControlTemplate.Triggers>
              <Trigger Property="IsExpanded"
                       Value="false">
                <Setter Property="Visibility"
                        TargetName="ItemsHost"
                        Value="Collapsed" />
              </Trigger>
              <Trigger Property="HasItems"
                       Value="false">
                <Setter Property="Visibility"
                        TargetName="Expander"
                        Value="Hidden" />
              </Trigger>
              <Trigger Property="IsSelected"
                       Value="true">
                <Setter Property="Background"
                        TargetName="Bd"
                        Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
                <Setter Property="Foreground"
                        Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
              </Trigger>
              <MultiTrigger>
                <MultiTrigger.Conditions>
                  <Condition Property="IsSelected"
                             Value="true" />
                  <Condition Property="IsSelectionActive"
                             Value="false" />
                </MultiTrigger.Conditions>
                <Setter Property="Background"
                        TargetName="Bd"
                        Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}" />
                <Setter Property="Foreground"
                        Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}" />
              </MultiTrigger>
              <Trigger Property="IsEnabled"
                       Value="false">
                <Setter Property="Foreground"
                        Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="VirtualizingPanel.IsVirtualizing"
                 Value="true">
          <Setter Property="ItemsPanel">
            <Setter.Value>
              <ItemsPanelTemplate>
                <VirtualizingStackPanel />
              </ItemsPanelTemplate>
            </Setter.Value>
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>

  <TreeView ItemsSource="{Binding}">
    <TreeView.Resources>
      <HierarchicalDataTemplate DataType="NestedContainer"
                                ItemsSource="{Binding ChildContainers}">
        <TextBlock Text="{Binding XID}" />
      </HierarchicalDataTemplate>
    </TreeView.Resources>
  </TreeView>
</Window>

Csharp相关问答推荐

EF Core的GbContent如何 suppress 所有CS 8618(不可为空属性)警告?

发布.NET框架项目将.NET核心元素注入到web. connect中

利用.NET 8中的AddStandardResilienceDeliveries和AddStandardHedgingDeliveries实现Resiliency

我可以 suppress 规则CS 9035一次吗?

在Dapper中使用IasyncEum重写GetAsyncEum方法

需要深入了解NpgSQL DateTimeOffset处理

当我使用NET6作为目标框架时,为什么DotNet使用NET8作为MS包?

try 在Blazor项目中生成html

Int和uint相乘得到LONG?

如何将ASP.NET Core 2.1(在.NET框架上运行)更新到较新的版本?

在允许溢出的情况下将小数转换为长

当前的文化决定了错误的文化

为基本审计设置Audit.EntityFramework.Core

EF核心新验证属性`DeniedValues`和`StringCompison`不起作用

在被Interactive Server切换后,Blazor SSR页面无法正确加载JS

获取混淆&Quot;模糊引用&Quot;错误

C#定时器回调对象上下文?

如何在更新数据库实体时忽略特定字段?

实体框架允许您具有筛选的属性吗?

有没有更好的方法来使用LINQ获取整行的计算组