在WPF(Windows Presentation Foundation)中,TreeView控件是一种常见的UI组件,用于展示层次结构的数据。默认情况下,TreeView以垂直列表的形式展示数据,但有时可能需要以不同的方式展示这些数据,例如类似于组织结构图的布局。本文将介绍如何通过自定义控件模板和样式来实现这种自定义布局。
本文假设读者已经具备了XAML、控件模板、样式、触发器、层次结构数据模板、数据绑定等WPF基础知识。如果对TreeView控件的另一种自定义布局方式感兴趣,可以阅读《WPF中TreeView控件的高级自定义布局》。
在开始编写XAML代码之前,先来看一下想要实现的效果。如果在TreeView中填充一些简单的数据,默认情况下它看起来可能很普通。以下是“修改前”的效果图:
显然,这不是一个令人印象深刻的数据表示方式。但是,通过自定义TreeViewItem的渲染方式以及TreeView定位其项的方式,同样的TreeView控件可以变成这样:
第一步是为TreeViewItem类创建自定义ControlTemplate。如果将该模板包装在一个类型化的Style中(即没有Key的Style),那么它将自动默认应用于每个TreeViewItem实例。TreeViewItem控件模板应该包含两个部分:一个名为'PART_Header'的ContentPresenter和一个ItemsPresenter。ContentPresenter用于显示项的内容,而ItemsPresenter用于显示其子项。
除了自定义TreeViewItem控件模板外,还需要修改TreeViewItem的ItemsPanel。为了让子项以水平行的形式显示,将TreeViewItem.ItemsPanel属性设置为具有水平方向的StackPanel。这个设置也应用在前面提到的类型化Style中。
让来看一下类型化Style的简化版本:
<Style TargetType="TreeViewItem">
<Style.Resources>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Name="Bd" Background="{StaticResource ItemAreaBrush}" BorderBrush="{StaticResource ItemBorderBrush}" BorderThickness="0.6" CornerRadius="8" Padding="6">
<ContentPresenter Name="PART_Header" ContentSource="Header" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ItemsPresenter Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Center" IsItemsHost="True" Margin="4,6" Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
最后一步是使TreeView将根项水平居中。这样做将在项之间提供对称性,如上面的截图所示。这一步很简单,只需将TreeView的ItemsPanel属性设置为HorizontalAlignment设置为'Center'的Grid即可。让来看一下包含自定义TreeView的Window的XAML代码:
<Window x:Class="CustomTreeViewLayout.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:CustomTreeViewLayout" Title="Custom TreeView" Height="350" Width="780" Loaded="OnLoaded" WindowStartupLocation="CenterScreen" FontSize="11">
<TreeView Name="tree">
<TreeView.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="OrgChartTreeViewItemStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
<HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding ChildNodes}">
<TextBlock Text="{Binding Text}" />
</HierarchicalDataTemplate>
</ResourceDictionary>
</TreeView.Resources>
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<Grid HorizontalAlignment="Center" IsItemsHost="True" />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
</TreeView>
</Window>
不会讨论用虚拟数据填充TreeView的代码。可以在本文顶部提供的源代码下载中查看该代码(以及所有其他代码)。