WPF 单DataGrid实现双层表头:顶层带排序、下层放搜索框
单DataGrid实现双层表头(列标题+搜索栏)方案
原有双Grid方案的核心问题
- 独立拆分的两个DataGrid排序状态完全不互通:上层只放列标题的DataGrid未绑定实际业务数据源,列对象没有关联排序属性路径,点击表头自然无法触发排序逻辑
- 双Grid布局需要手动对齐列宽、边距、滚动位置,窗口缩放时极易出现列错位的问题,维护成本极高
- 要实现排序交互在上层表头的效果,本质上绕不开“列头所在的DataGrid必须绑定实际可排序的数据源”这个前提
优化实现思路
完全取消双Grid叠加的布局,仅保留1个绑定实际业务数据的DataGrid,在每个列的HeaderTemplate内实现纵向双层结构:
- 第一层放置列标题文本,完全保留DataGrid原生的列头点击排序交互,不需要额外写排序事件
- 第二层放置对应列的搜索TextBox,搜索框本身会拦截自身区域的鼠标输入事件,不会影响周围列标题区域的排序触发
修正后的代码实现
XAML布局
<Grid> <DataGrid x:Name="gridd" AutoGenerateColumns="False" Margin="93.195,102.655,0,0" CanUserSortColumns="True" RowHeaderWidth="0"> <DataGrid.Resources> <!-- 沿用原有表头视觉样式 --> <Style TargetType="{x:Type DataGridColumnHeader}"> <Setter Property="Background" Value="Gray"/> <Setter Property="Foreground" Value="White"/> <Setter Property="BorderThickness" Value="0,0,1,0" /> <Setter Property="BorderBrush" Value="White"/> <Setter Property="Padding" Value="5 0 0 0"/> <Setter Property="Height" Value="Auto"/> </Style> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Width="*" Binding="{Binding Id}" SortMemberPath="Id"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock Text="Artikelnummer" FontSize="10"/> <TextBox x:Name="SearchBox_Id" Margin="0,4,0,0"/> </StackPanel> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> <DataGridTextColumn Width="*" Binding="{Binding Name}" SortMemberPath="Name"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock Text="Artikelnummer" FontSize="10"/> <TextBox x:Name="SearchBox_Name" Margin="0,4,0,0"/> </StackPanel> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> <DataGridTextColumn Width="*" Binding="{Binding Birthday}" SortMemberPath="Birthday"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock Text="Matchcode" FontSize="10"/> <TextBox x:Name="SearchBox_Birthday" Margin="0,4,0,0"/> </StackPanel> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> <DataGridTextColumn Width="*" Binding="{Binding ort}" SortMemberPath="ort"> <DataGridTextColumn.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock Text="Hersteller" FontSize="10"/> <TextBox x:Name="SearchBox_Ort" Margin="0,4,0,0"/> </StackPanel> </DataTemplate> </DataGridTextColumn.HeaderTemplate> </DataGridTextColumn> <DataGridCheckBoxColumn Width="*" Binding="{Binding IsSelected}" SortMemberPath="IsSelected"> <DataGridCheckBoxColumn.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock Text="CheckBox" FontSize="10"/> <TextBox x:Name="SearchBox_Check" Margin="0,4,0,0"/> </StackPanel> </DataTemplate> </DataGridCheckBoxColumn.HeaderTemplate> </DataGridCheckBoxColumn> </DataGrid.Columns> </DataGrid> </Grid>
后台C#逻辑
public MainWindow() { InitializeComponent(); List<User> users = new List<User> { new User() { Id = 1, Name = "John Doe", ort="Berlin", Birthday = new DateTime(1971, 7, 23), alter=18, IsSelected = false }, new User() { Id = 2, Name = "Jane Doe", ort = "Köln", Birthday = new DateTime(1974, 1, 17), alter = 28, IsSelected = true }, new User() { Id = 3, Name = "Sammy Doe", ort = "Bayern", Birthday = new DateTime(1991, 9, 2), alter = 38, IsSelected = false } }; // 用CollectionViewSource包装数据源,保证排序、过滤状态正常生效 CollectionViewSource viewSource = new CollectionViewSource(); viewSource.Source = users; gridd.ItemsSource = viewSource.View; } public class User { public int Id { get; set; } public string Name { get; set; } public string ort { get; set; } public DateTime Birthday { get; set; } public int alter { get; set; } public bool IsSelected { get; set; } }
落地注意事项
- 不需要额外编写排序触发代码:点击列标题区域会自动触发对应列的升/降序切换,原生的排序方向箭头会正常显示
- 列宽完全自动适配窗口大小,不存在双Grid的列错位问题
- 后续扩展列级搜索时,只需要给每个TextBox绑定TextChanged事件,通过当前DataGrid的CollectionView添加过滤规则即可,不需要调整布局结构
内容的提问来源于stack exchange,提问作者user15530832
相关产品推荐
相关产品推荐

