C# WPF TreeView HierarchicalDataTemplate - 在列表末尾回显父节点文本

C# WPF TreeView HierarchicalDataTemplate - Echo parent node text at the end of the list

我正在寻找一种方法来使用 TreeView(WPF、c#)在元素列表的末尾回显父节点名称。

我正在使用的信息列表可能很长,我想向用户显示他们所处的内容 "category",而无需向上滚动到列表顶部。我有一个 HierarchicalDataTemplate 可以执行一些简单的字体格式设置,但我看不到 "closing" 节点的放置位置,也看不到如何获取它的文本。

这是我正在尝试做的事情的简化示例:

您至少可以通过两种方式进行。第一种方法是重新定义整个树视图样式。你可以找到一些如何做的例子here(这个例子没有解决你的问题,但你可以得到一般的想法)。这个解决方案的缺点是代码非常大,并且在每个系统上看起来都一样,比如 Windows XP of Windows 10,因为在正常情况下每个系统都使用自己的风格。

我更喜欢用另一种方式来解决这样的问题。我从 C# 代码进行更改。例如,在您的情况下,您可以在 Visual Studio Xaml 可视化器中看到树视图的结构:

每个树视图项目都包含一个 ToggleButton - 用于在网格的第一行展开和折叠。接下来是一个边框,其中包含带有项目内容的 TextBlock(在您的情况下,它可以是数据模板中的另一个控件,我以 textblock 为例)。它放在同一个第一行。在网格的第二行有包含子元素的 ItemsPresenter。

因此,目标是在网格的两行再添加一行,并在其中放置一个文本块。

首先,将 ItemContainerStyle 添加到树视图并在那里设置事件处理程序:

<TreeView.ItemContainerStyle>
  <Style TargetType="{x:Type TreeViewItem}">
    <EventSetter Event="Loaded" Handler="Item_Loaded"/>
  </Style>
</TreeView.ItemContainerStyle>

然后在后面的代码中:

void Item_Loaded(object sender, RoutedEventArgs e) {
    // Find the main grid of this TreeView item.
    Grid grid = FindVisualChild<Grid>((DependencyObject) sender);

    // Add new row, because it has only 2 and we need 3
    grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

    // Get the content to put into the textblock (or another control you are using in datatemplate)
    string text = ((Node) grid.DataContext).Name;

    // I'm using TextBlock to show example, you can use your own control
    TextBlock tb = new TextBlock {
        Text = text,
        Foreground = new SolidColorBrush(Colors.Gray),
    };

    grid.Children.Add(tb);

    // Visibility of our modification depends on IsExpanded and amount of child elements. If itemcontainer is collapsed or doesn't have children, we don't show this modification.
    bool flag = false;
    grid.SizeChanged += (sender1, e1) => {
        if (flag) {
            return;
        }
        flag = true;
        tb.Visibility = grid.RowDefinitions[1].ActualHeight > 0
        ? Visibility.Visible
        : Visibility.Collapsed;
        flag = false;
    };

    // Set the position of the added part
    tb.SetValue(Grid.RowProperty, 2);
    tb.SetValue(Grid.ColumnProperty, 1);
}

这不是理想的解决方案,Xaml在不同的系统上可能会有所不同,因此此功能可能会略有不同。我只检查了 windows 10.

FindVisualChild函数是这样的:

static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject {
    for (int i = 0 ; i < VisualTreeHelper.GetChildrenCount(obj) ; i++) {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child is T) {
            return (T) child;
        }
        T childOfChild = FindVisualChild<T>(child);
        if (childOfChild != null) {
            return childOfChild;
        }
    }
    return null;
}

我实际上会采用不同的方法。实际上有几个。

第一个是 re-template parent 节点的 TreeViewItem。考虑到它的 'meat' 只是一个带有披露切换的网格,一个用于 header 的 ContentPresenter(这是树中的 'item')和一个 ItemsControl(及其 ItemsSource绑定到节点的 children),它只是嵌套了更多这些容器。只需将第三行添加到模板中的网格,插入另一个内容展示器并将其绑定也设置为 header,即可完成。您可以在那里使用简单的样式来做到这一点。根本不需要 code-behind。

您可以做的第二件事是添加一个完全独立的 'crumbtrail' 控件来显示关联树中的选定分支。他们真的很容易做到。在树中选择项目时,沿着层次结构向上移动,将项目添加到列表,然后将其绑定到具有水平堆栈面板的列表框,用于其 ItemsPanel。当然,您需要对其进行一些模板化以添加分隔符等,但它也会为您提供 'clickable' 踪迹。

不管怎样,这只是两个想法。也许他们甚至可以激发三分之一。