如何让文本框填充树视图项

How to get textbox to fill a treeview item

我有一个 TreeView,其中有 3 个级别,由 HierarchicalDataTemplate 驱动。这是部分 xaml 清单:

<TreeView
    Name="TreeView"
    ItemsSource="{Binding GTOs}"
    ScrollViewer.HorizontalScrollBarVisibility="Hidden">
    <TreeView.ItemsPanel>
        <ItemsPanelTemplate>
            <!--  This binding prevents the TreeView from slipping over to the right when an item is clicked.  -->
            <StackPanel MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ContentPresenter, AncestorLevel=1}}" />
        </ItemsPanelTemplate>
    </TreeView.ItemsPanel>

    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:TreeGTOViewModel}" ItemsSource="{Binding Children}">
            <TextBlock
                Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeView}}, Path=ActualWidth}"
                MouseDown="TextBlock_MouseDown"
                Tag="{Binding Uri}"
                Text="{Binding Title}" />
        </HierarchicalDataTemplate>

TextBlock 的 Width 属性上的 Binding 确保每个 TreeViewItem 都采用 TreeView 容器的整个宽度(而不仅仅是文本框的宽度)。结合 Material 设计样式,当鼠标悬停在树中的特定项目上时,这会产生很好的阴影效果。

只有一个问题。虽然我可以让宽度假定 TreeViewItem 的整个宽度,但我似乎无法对高度做同样的事情。这意味着用户可以在 TreeView 项目的边缘附近单击并查看所有效果,就像单击该项目一样,但不会触发 MouseDown 事件。

这是应该可点击的区域(红色虚线):

这是实际可点击的区域(文本框的外边框):

我尝试了多种不同的方法,包括这个:

VerticalAlignment="Stretch"

还有这个:

Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}, Path=ActualHeight}"

但它们要么没有效果,要么以意想不到的方式破坏了 TreeView。我认为部分问题是 ItemsPanelTemplate 中使用的 StackPanel 通常的高度怪异。

如何让每个 TreeViewItem 中的 TextBox 假定实际 TreeViewItem 的高度?

下面是可视化树相关部分的图像,从 TreeView 到可点击的文本框。 Ripple 元素是一个 MaterialDesignThemes.Wpf.Ripple 对象。

好吧,我通过不同的途径解决了这个问题。我不在文本块上挂钩 MouseDown 事件,而是在树视图上挂钩 SelectedItemChanged 事件。

    <TreeView
        ItemsSource="{Binding GTOs}"
        SelectedItemChanged="TreeView_SelectedItemChanged">

SelectedItemChanged 事件的 RoutedPropertyChangedEventArgs 包含特定 TreeViewItem 的 ViewModel,它产生了导航所需的 Uri 属性(我之前绑定了这个属性 到文本框的标签 属性).

新的事件处理程序如下所示:

private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    dynamic viewModel = e.NewValue;
    var uri = viewModel.Uri;
    (DataContext as TreeViewModel).Navigate(uri);
}

树的每一层都包含不同的 ViewModel 类型,但它们都有一个 Uri 属性,因此 dynamic 绑定提供了所需的 "don't care what type it is" 行为。

我现在可以从 TextBlock 中删除 Tag 绑定和 MouseDown 事件。

    <HierarchicalDataTemplate DataType="{x:Type local:TreeGTOViewModel}" ItemsSource="{Binding Children}">
        <TextBlock
            Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeView}}, Path=ActualWidth}"
            Text="{Binding Title}" />

树视图项现在可以正确响应,无论在何处单击它们。

我制作了一个非常简单的 MahApp 测试项目,其中包含一个 treeview 和一个 treeviewitem(无绑定)。

在实时可视化树中,PART_Header 的 VerticalAlignment 值为 Center。如果我将其更改为 Stretch(使用 Live 属性 Explorer),文本块会垂直填充 space。

因此,我使用 VS 中的文档大纲来编辑树视图模板的副本。在里面,我看到了 <Setter Property="VerticalContentAlignment" Value="Center">,所以我决定在我的 TreeView 上将它设置为 Stretch,它似乎起作用了。

<TreeView x:Name="TreeView"
  ScrollViewer.HorizontalScrollBarVisibility="Hidden"
  VerticalContentAlignment="Stretch">