You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF C#:带多列及表头的TreeView(HierarchicalTemplate)实现问题

带多列布局的TreeView解决方案

问题说明

需要实现带层级结构的TreeView,展示数据源的4个属性,采用4列布局规则:

  • 第一列宽度设为Auto,随TreeView展开/折叠自动调整宽度
  • 第二列宽度设为*,自动填充剩余空间
  • 第三、四列固定100单位宽度
  • 顶部设置标题行,标题列与TreeView内容列宽严格同步

现有布局存在问题:TreeView仅占用单列无法展示多属性;调整列数后列宽不同步,展开折叠时布局错乱。

解决方案核心调整

  1. TreeView布局修正:让TreeView占据容器Grid的所有列,而非限制在单列表格
  2. 数据模板改用Grid:在HierarchicalDataTemplate中使用Grid实现4列布局,匹配标题列的宽度规则
  3. 列宽同步:通过SharedSizeGroup绑定标题列与TreeView内容列的宽度,确保完全同步
  4. TreeViewItem模板优化:设置TreeViewItem内容横向拉伸,填满可用空间

修改后的完整代码

XAML代码

<Window x:Class="TreeView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TreeView"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="900">
    <Window.Resources>
        <Style x:Key="TreeViewItemStyle1" TargetType="{x:Type TreeViewItem}">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <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="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TreeViewItem}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition MinWidth="19" Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"/>
                            <Border x:Name="Bd" Grid.Column="1" HorizontalAlignment="Stretch" Background="Transparent" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                                <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="Stretch" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1" />
                        </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>
                        </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>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
    
        <!-- 标题行 -->
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
                <ColumnDefinition Width="*" SharedSizeGroup="Col2"/>
                <ColumnDefinition Width="100" SharedSizeGroup="Col3"/>
                <ColumnDefinition Width="100" SharedSizeGroup="Col4"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="Column1" Padding="5"/>
            <TextBlock Grid.Column="1" Text="Column2" Padding="5"/>
            <TextBlock Grid.Column="2" Text="Column3" Padding="5"/>
            <TextBlock Grid.Column="3" Text="Column4" Padding="5"/>
        </Grid>

        <!-- TreeView容器 -->
        <Grid x:Name="TreeViewGrid" Grid.Row="1">
            <TreeView x:Name="treeView" 
                      ItemContainerStyle="{StaticResource TreeViewItemStyle1}" 
                      HorizontalContentAlignment="Stretch">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding Children}">
                        <!-- 每个TreeViewItem的4列布局 -->
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" SharedSizeGroup="Col1"/>
                                <ColumnDefinition Width="*" SharedSizeGroup="Col2"/>
                                <ColumnDefinition Width="100" SharedSizeGroup="Col3"/>
                                <ColumnDefinition Width="100" SharedSizeGroup="Col4"/>
                            </Grid.ColumnDefinitions>

                            <Border Background="Green" CornerRadius="2" Padding="5">
                                <TextBlock Text="{Binding Name}"/>
                            </Border>
                            <Border Grid.Column="1" Background="Green" CornerRadius="2" Padding="5">
                                <TextBlock Text="{Binding Prop2}"/>
                            </Border>
                            <Border Grid.Column="2" Background="Green" CornerRadius="2" Padding="5">
                                <TextBlock Text="{Binding Prop3}"/>
                            </Border>
                            <Border Grid.Column="3" Background="Green" CornerRadius="2" Padding="5">
                                <TextBlock Text="{Binding Prop4}"/>
                            </Border>
                        </Grid>
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>
        </Grid>
    </Grid>
</Window>

后台代码

using System.Collections.ObjectModel;
using System.Windows;

namespace TreeView
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            LoadTreeView();
        }

        private void LoadTreeView()
        {
            // 构建包含4个属性的测试数据
            var rootNode = new Node 
            { 
                Name = "Root", 
                Prop2 = "Root Prop2", 
                Prop3 = "Root Prop3", 
                Prop4 = "Root Prop4" 
            };

            var child1 = new Node 
            { 
                Name = "Child 1", 
                Prop2 = "Child1 Prop2", 
                Prop3 = "Child1 Prop3", 
                Prop4 = "Child1 Prop4" 
            };
            var child2 = new Node 
            { 
                Name = "Child 2", 
                Prop2 = "Child2 Prop2", 
                Prop3 = "Child2 Prop3", 
                Prop4 = "Child2 Prop4" 
            };

            rootNode.Children.Add(child1);
            rootNode.Children.Add(child2);

            var child1_1 = new Node 
            { 
                Name = "Child 1.1", 
                Prop2 = "Child1.1 Prop2", 
                Prop3 = "Child1.1 Prop3", 
                Prop4 = "Child1.1 Prop4" 
            };
            var child1_2 = new Node 
            { 
                Name = "Child 1.2", 
                Prop2 = "Child1.2 Prop2", 
                Prop3 = "Child1.2 Prop3", 
                Prop4 = "Child1.2 Prop4" 
            };
            child1.Children.Add(child1_1);
            child1.Children.Add(child1_2);

            var child2_1 = new Node 
            { 
                Name = "Child 2.1", 
                Prop2 = "Child2.1 Prop2", 
                Prop3 = "Child2.1 Prop3", 
                Prop4 = "Child2.1 Prop4" 
            };
            var child2_2 = new Node 
            { 
                Name = "Child 2.2", 
                Prop2 = "Child2.2 Prop2", 
                Prop3 = "Child2.2 Prop3", 
                Prop4 = "Child2.2 Prop4" 
            };
            child2.Children.Add(child2_1);
            child2.Children.Add(child2_2);

            treeView.ItemsSource = new ObservableCollection<Node> { rootNode };
        }
    }

    public class Node
    {
        public string Name { get; set; }
        public string Prop2 { get; set; }
        public string Prop3 { get; set; }
        public string Prop4 { get; set; }
        public ObservableCollection<Node> Children { get; set; }

        public Node()
        {
            Children = new ObservableCollection<Node>();
        }
    }
}

关键调整说明

  • SharedSizeGroup绑定:标题列与TreeView内容列使用相同的分组名称,确保列宽完全同步
  • 横向拉伸设置:将TreeViewItem和ContentPresenter的HorizontalAlignment设为Stretch,让内容填满可用宽度
  • Grid替换DockPanel:用Grid实现精确的4列布局,对应每个属性的展示位置
  • 移除固定宽度:删除TreeView的固定宽度设置,让其自适应容器宽度

内容的提问来源于stack exchange,提问作者AnonNoob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:55:54