1、在WPF中TreeView默认不支持固定表头的我们可以修改样式实现固定表头
新建一个TreeListView类 然后继承TreeView代码如下
public class TreeListView : TreeView,IDisposable{public TreeListView(){//this.Loaded += TreeListView_Loaded;//this.SizeChanged += TreeListView_SizeChanged;//SourceUpdated += TreeListView_SourceUpdated; }private void TreeListView_SourceUpdated(object sender, System.Windows.Data.DataTransferEventArgs e){}double fixedColumnsWidth = 0;//int resizableColumnCount = 0;List<int> resizableColumnCount=new List<int>();GridViewHeaderRowPresenter gridViewHeaderRowPresenter;bool shub=false;private void TreeListView_SizeChanged(object sender, SizeChangedEventArgs e){fixedColumnsWidth = 0;var treeListView = sender as TreeListView; // 将 DependencyObject 转换为具体类型if (treeListView != null){double wh = treeListView.ActualWidth;for (int i = 0;i < treeListView.Columns.Count; i++){if (treeListView.Columns[i].Width > 0){if (resizableColumnCount.Contains(i)==false)fixedColumnsWidth += treeListView.Columns[i].Width;}else{if (resizableColumnCount.Any() == false)resizableColumnCount.Add(i);}}// 计算剩余空间double remainingWidth = wh - fixedColumnsWidth;// 如果有可调整宽度的列,将剩余空间均分给它们if (resizableColumnCount.Any()){double widthPerResizableColumn = remainingWidth / resizableColumnCount.Count;foreach (int i in resizableColumnCount){if (resizableColumnCount.Contains(i)){treeListView.Columns[i] .Width= widthPerResizableColumn;}}}}}private void TreeListView_Loaded(object sender, RoutedEventArgs e){gridViewHeaderRowPresenter= this.Template.FindName("HeaderPresenter", this) as GridViewHeaderRowPresenter;if(gridViewHeaderRowPresenter != null ){gridViewHeaderRowPresenter.PreviewMouseLeftButtonDown += GridViewHeaderRowPresenter_PreviewMouseLeftButtonDown;gridViewHeaderRowPresenter.PreviewMouseMove += GridViewHeaderRowPresenter_PreviewMouseMove;gridViewHeaderRowPresenter.PreviewMouseLeftButtonUp += GridViewHeaderRowPresenter_PreviewMouseLeftButtonUp;}}private void GridViewHeaderRowPresenter_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e){shub=false;}private void GridViewHeaderRowPresenter_PreviewMouseMove(object sender, MouseEventArgs e){if (shub){//var d= e.GetPosition(gridViewHeaderRowPresenter);// // 找到当前被点击的列// var hitTestResult = VisualTreeHelper.HitTest(gridViewHeaderRowPresenter, d);// if (hitTestResult != null)// {// // 获取被点击的列标题// var columnHeader = hitTestResult.VisualHit as GridViewColumn;// if (columnHeader != null)// {// var gridView = (GridView)gridViewHeaderRowPresenter.DataContext;// var columns = gridView.Columns;// // 检查是否是最后一列// //if (columnHeader.c == columns.Last())// //{// // // 禁用拖动行为// // e.Handled = true;// //}// }// } }}private void GridViewHeaderRowPresenter_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e){shub = true;}protected override System.Windows.DependencyObjectGetContainerForItemOverride(){return new TreeListViewItem();}protected override boolIsItemItsOwnContainerOverride(object item){return item is TreeListViewItem;}/// <summary>/// Gets or sets the collection of System.Windows.Controls.GridViewColumn /// objects that is defined for this TreeListView./// </summary>public GridViewColumnCollection Columns{get { return (GridViewColumnCollection)GetValue(ColumnsProperty); }set { SetValue(ColumnsProperty, value); }}// Using a DependencyProperty as the backing store for Columns. This enables animation, styling, binding, etc...public static readonly System.Windows.DependencyProperty ColumnsProperty =System.Windows.DependencyProperty.Register("Columns", typeof(GridViewColumnCollection),typeof(TreeListView),new PropertyMetadata(OnColumnsChanged));////public Command PreviewMouseLeftButtonDownl//{// get { return (Command)GetValue(PreviewMouseLeftButtonDownlProperty); }// set { SetValue(PreviewMouseLeftButtonDownlProperty, value); }//}//// Using a DependencyProperty as the backing store for PreviewMouseLeftButtonDownl. This enables animation, styling, binding, etc...//public static readonly DependencyProperty PreviewMouseLeftButtonDownlProperty =// DependencyProperty.Register("PreviewMouseLeftButtonDownl", typeof(Command), typeof(GridViewColumnCollection), new PropertyMetadata(PreviewMouseLeftButtonDownlcleck));//private static void PreviewMouseLeftButtonDownlcleck(DependencyObject d, DependencyPropertyChangedEventArgs e)//{//}private static void OnColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){// 在此处理属性更改的逻辑var treeListView = d as GridViewHeaderRowPresenter; // 将 DependencyObject 转换为具体类型if (treeListView != null){foreach (var columnHeader in treeListView.Columns){// (columnHeader.).PreviewMouseLeftButtonUp += ColumnHeader_PreviewMouseLeftButtonDown;//columnHeader.PreviewMouseMove += ColumnHeader_PreviewMouseMove;//columnHeader.PreviewMouseLeftButtonUp += ColumnHeader_PreviewMouseLeftButtonUp; }// 在此处添加您的处理逻辑,例如通知其他控件 }}public void Dispose(){}}public class TreeListViewItem : TreeViewItem{/// <summary>/// Item's hierarchy in the tree/// </summary>public int Level{get{if (_level == -1){TreeListViewItem parent =ItemsControl.ItemsControlFromItemContainer(this)as TreeListViewItem;_level = (parent != null) ? parent.Level + 1 : 0;}return _level;}}protected override DependencyObjectGetContainerForItemOverride(){return new TreeListViewItem();}protected override bool IsItemItsOwnContainerOverride(object item){return item is TreeListViewItem;}private int _level = -1;}
样式如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:convert="clr-namespace:TreeListViewSample.Converters"xmlns:local="clr-namespace:TreeListViewSample.Controls"><!--Converter for Indentation of items--><convert:LevelToIndentConverter x:Key="LevelIndentConverter" /><Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}"><Setter Property="Width" Value="19"/><Setter Property="Height" Value="13"/><Setter Property="Focusable" Value="False"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type ToggleButton}"><Border Width="19" Height="13" Background="Transparent"><Border Width="9" Height="9"BorderThickness="1"BorderBrush="#FF7898B5"CornerRadius="1"SnapsToDevicePixels="true"><Border.Background><LinearGradientBrush StartPoint="0,0" EndPoint="1,1"><LinearGradientBrush.GradientStops><GradientStop Color="White" Offset=".2"/><GradientStop Color="#FFC0B7A6" Offset="1"/></LinearGradientBrush.GradientStops></LinearGradientBrush></Border.Background><Path x:Name="ExpandPath"Margin="1,1,1,1"Fill="Black"Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z"/></Border></Border><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter Property="Data" TargetName="ExpandPath" Value="M 0 2 L 0 3 L 5 3 L 5 2 Z"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style><DataTemplate x:Key="CellTemplate_Name"><DockPanel><ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" Margin="{Binding Path=Level,Converter={StaticResource LevelIndentConverter},RelativeSource={RelativeSource AncestorType={x:Type local:TreeListViewItem}}}"IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource AncestorType={x:Type local:TreeListViewItem}}}"ClickMode="Press"/><!--首列绑定值--><TextBlock Text="{Binding Name}"/></DockPanel><DataTemplate.Triggers><DataTrigger Binding="{Binding Path=HasItems,RelativeSource={RelativeSource AncestorType={x:Type local:TreeListViewItem}}}" Value="False"><Setter TargetName="Expander" Property="Visibility" Value="Hidden"/></DataTrigger></DataTemplate.Triggers></DataTemplate><!--<ImageBrush x:Key="gvHeaderBackground" ImageSource="/TreeListViewSample;component/Images/list_header_bk.png" />--><Style x:Key="gvColumnHeaderStyle" TargetType="{x:Type GridViewColumnHeader}"><Style.Setters><Setter Property="Height" Value="34" /><Setter Property="Background"><Setter.Value><LinearGradientBrush StartPoint="0,0" EndPoint="1,1"><GradientStop Color="LightBlue" Offset="0" /><GradientStop Color="Gray" Offset="1" /></LinearGradientBrush></Setter.Value></Setter></Style.Setters></Style><!--<GridViewColumnCollection x:Key="gvColumns"><GridViewColumn Header="姓名"CellTemplate="{StaticResource CellTemplate_Name}" Width="150" /><GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="80" /><GridViewColumn Header="性别" DisplayMemberBinding="{Binding Sex}" Width="80" /><GridViewColumn Header="职务"DisplayMemberBinding="{Binding Duty}" Width="100" /></GridViewColumnCollection>--><!--交替行样式--><AlternationConverter x:Key="conBack"><SolidColorBrush>YellowGreen</SolidColorBrush><SolidColorBrush>SkyBlue</SolidColorBrush></AlternationConverter><Style TargetType="{x:Type local:TreeListViewItem}"><Setter Property="FontSize" Value="13" /><Setter Property="Background" Value="Transparent" /><Setter Property="SnapsToDevicePixels" Value="True" /><Setter Property="HorizontalContentAlignment" Value="Stretch" /><Setter Property="VerticalContentAlignment" Value="Center" /><Setter Property="IsSelected" Value="{Binding IsSelected}" /><Setter Property="IsExpanded" Value="{Binding IsExpanded}" /><Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self},Path=(local:TreeListView.AlternationIndex),Converter={StaticResource conBack}}" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeListViewItem}"><StackPanel><Border x:Name="outerBorder"BorderThickness="0,0,0,1"BorderBrush="#FFFFFFFD"Margin="0" Padding="0" Height="30"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"SnapsToDevicePixels="True"><Border x:Name="innerBorder"BorderThickness="0,0,0,1"BorderBrush="#FFC6C6C6"Background="{TemplateBinding Background}"Padding="{TemplateBinding Padding}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"SnapsToDevicePixels="True"><GridViewRowPresenter x:Name="PART_Header"Content="{TemplateBinding Header}" Columns="{Binding Columns,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeListView}}"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/></Border></Border><ItemsPresenter x:Name="ItemsHost" /></StackPanel><ControlTemplate.Triggers><!--<Trigger Property="local:TreeListView.AlternationIndex" Value="1"> Columns="{Binding Columns, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TreeListView}"<Setter Property="Background" Value="#FFE1E4F7"></Setter></Trigger>--><Trigger Property="IsMouseOver" Value="true" SourceName="innerBorder"><Setter Property="Foreground" Value="White"/><Setter Property="Background" Value="#FFC66152" TargetName="innerBorder"/></Trigger><Trigger Property="IsSelected" Value="true"><!--<Setter TargetName="Bd" Property="Background"Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/><Setter Property="Foreground"Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>--><Setter Property="Foreground" Value="White"/><Setter Property="Background" Value="#FFC66152" TargetName="innerBorder"/></Trigger><Trigger Property="IsExpanded" Value="false"><Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/></Trigger><Trigger Property="IsEnabled" Value="false"><Setter Property="Foreground"Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/></Trigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsSelected" Value="true"/><Condition Property="IsSelectionActive" Value="false"/></MultiTrigger.Conditions><Setter TargetName="innerBorder" Property="Background"Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/><Setter Property="Foreground"Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/></MultiTrigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="HasHeader" Value="false"/><Condition Property="Width" Value="Auto"/></MultiTrigger.Conditions><Setter TargetName="PART_Header" Property="MinWidth" Value="75"/></MultiTrigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="HasHeader" Value="false"/><Condition Property="Height" Value="Auto"/></MultiTrigger.Conditions><Setter TargetName="PART_Header" Property="MinHeight" Value="19"/></MultiTrigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style><Style TargetType="{x:Type local:TreeListView}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:TreeListView}"><!--Create a standard border around the 'TreeListView'.--><Border Padding="{TemplateBinding Padding}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"Background="{TemplateBinding Background}"><!--ScrollViewer providing horizontal scrolling functionality for both, content and headers.--><!--<ScrollViewer HorizontalScrollBarVisibility="Auto" CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"VerticalScrollBarVisibility="Auto" Focusable="False">--><DockPanel><!--The header row. PreviewMouseLeftButtonDown="{Binding }" PreviewMouseMove="" PreviewMouseLeftButtonUp=""--><GridViewHeaderRowPresenter DockPanel.Dock="Top" SnapsToDevicePixels="True" x:Name="HeaderPresenter"Columns="{Binding Columns,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TreeListView}}"ColumnHeaderContainerStyle="{StaticResource gvColumnHeaderStyle}"></GridViewHeaderRowPresenter><!--ScrollViewer providing vertical scrolling Columns="{StaticResource gvColumns}"functionality for the content.--><ScrollViewer HorizontalScrollBarVisibility="Auto" CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"VerticalScrollBarVisibility="Auto" Focusable="False"><!--ItemsPresenter containg the content.--><ItemsPresenter /></ScrollViewer></DockPanel><!--</ScrollViewer>--></Border></ControlTemplate></Setter.Value></Setter></Style><!--<Style TargetType="GridViewHeaderRowPresenter"><Setter Property="SnapsToDevicePixels" Value="True"/><Setter Property="Background" Value="LightBlue"/>--><!-- 修改背景颜色 --><!--<Setter Property="Foreground" Value="Black"/>--><!-- 修改字体颜色 --><!--<Setter Property="FontSize" Value="14"/>--><!-- 修改字体大小 --><!--<Setter Property="FontWeight" Value="Bold"/>--><!-- 修改字体粗细 --><!--<Setter Property="HorizontalContentAlignment" Value="Left"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="Height" Value="30"/>--><!-- 修改行高 --><!--<Setter Property="Padding" Value="8"/>--><!-- 修改内边距 --><!--<Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:TypeGridViewHeaderRowPresenter}"><GridViewHeaderRowPresenterColumns="{TemplateBindingGridView.ColumnHeaderContainerStyle}"Columns="{TemplateBindingGridView.Columns}"ColumnHeaderContextMenu="{TemplateBindingGridView.ColumnHeaderContextMenu}"ColumnHeaderToolTip="{TemplateBindingGridView.ColumnHeaderToolTip}"KeyboardNavigation.TabNavigation="Local"SnapsToDevicePixels="{TemplateBindingSnapsToDevicePixels}"/></ControlTemplate></Setter.Value></Setter></Style>--> </ResourceDictionary>
添加测试数据的代码如下
namespace TreeListViewSample.ViewModels {public class MainViewModel : ViewModelBase{private ObservableCollection<Staff> _StaffList = new ObservableCollection<Staff>();public ObservableCollection<Staff> StaffList{get { return _StaffList; }set{_StaffList = value;this.RaisePropertyChanged("StaffList");}}public MainViewModel(){for (int i = 0; i < 10000; i++){Staff staff = new Staff(){Name = "张三",Age = 30,Sex = "男",Duty = "经理",IsExpanded = true};Staff staff2 = new Staff(){Name = "张三1",Age = 21,Sex = "男",Duty = "员工",IsExpanded = true};Staff staff3 = new Staff(){Name = "张三11",Age = 21,Sex = "男",Duty = "员工"};staff2.StaffList.Add(staff3);staff3 = new Staff(){Name = "张三22",Age = 21,Sex = "女",Duty = "员工"};staff2.StaffList.Add(staff3);staff.StaffList.Add(staff2);staff2 = new Staff(){Name = "张三2",Age = 22,Sex = "女",Duty = "员工"};staff.StaffList.Add(staff2);staff2 = new Staff(){Name = "张三3",Age = 23,Sex = "女",Duty = "员工"};staff.StaffList.Add(staff2);StaffList.Add(staff);staff = new Staff(){Name = "李四",Age = 31,Sex = "男",Duty = "副经理"};staff2 = new Staff(){Name = "李四1",Age = 24,Sex = "女",Duty = "员工"};staff.StaffList.Add(staff2);staff2 = new Staff(){Name = "李四2",Age = 25,Sex = "女",Duty = "员工"};staff.StaffList.Add(staff2);staff2 = new Staff(){Name = "李四3",Age = 26,Sex = "男",Duty = "员工"};staff.StaffList.Add(staff2);StaffList.Add(staff);staff = new Staff(){Name = "王五",Age = 32,Sex = "女",Duty = "组长"};staff2 = new Staff(){Name = "王五1",Age = 27,Sex = "女",Duty = "员工"};staff.StaffList.Add(staff2);staff2 = new Staff(){Name = "王五2",Age = 28,Sex = "女",Duty = "员工"};staff.StaffList.Add(staff2);StaffList.Add(staff);} }public void ChangeNode1Value(){foreach (Staff staff in this.StaffList){staff.Age += 1;staff.Sex = staff.Sex == "男" ? "女" : "男";}}public void ChangeNode2Value(){foreach (Staff staff in this.StaffList){foreach (Staff staff2 in staff.StaffList){staff2.Age += 1;staff2.Sex = staff2.Sex == "男" ? "女" : "男";}}}} }
最后 这里我们开启了虚拟化添加再多数据界面也不会卡顿
<local:TreeListView x:Name="treeList" ScrollViewer.CanContentScroll="True"VirtualizingStackPanel.IsVirtualizing="True"VirtualizingStackPanel.VirtualizationMode="Recycling"Background="#FFE9E9E9" BorderBrush="#FF96DFFF"AlternationCount="2" ItemsSource="{Binding StaffList}"><local:TreeListView.ItemsPanel><ItemsPanelTemplate><VirtualizingStackPanel x:Name="ItemsHost" IsItemsHost="True" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" Height="Auto"/></ItemsPanelTemplate></local:TreeListView.ItemsPanel><local:TreeListView.Columns><GridViewColumnCollection><GridViewColumn Header="姓名" CellTemplate="{StaticResource CellTemplate_Name}" Width="200" ></GridViewColumn><GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="80" /><GridViewColumn Header="性别" DisplayMemberBinding="{Binding Sex}" Width="80" /><GridViewColumn Header="职务"DisplayMemberBinding="{Binding Duty}" Width="150"/></GridViewColumnCollection></local:TreeListView.Columns><local:TreeListView.ItemTemplate><HierarchicalDataTemplate ItemsSource="{Binding StaffList}" /></local:TreeListView.ItemTemplate></local:TreeListView>
最后效果