WPF DataGrid出现额外列头:如何移除显示ViewModel名称的列头
解决WPF DataGrid额外列头显示ViewModel名称及筛选按钮的问题
我明白你的困扰——那个额外冒出来的列头确实烦人,而且你不想通过重写ToString()这种hack方式解决。先给你说清楚问题根源:你看到的这个额外列头其实是DataGrid自动生成的**填充列(Filler Column)**的表头,它的作用是填满DataGrid控件的剩余宽度。因为你给所有DataGridColumnHeader都应用了同一个带筛选按钮的模板,而这个填充列的Content绑定的是你的CollectionView实例,所以才会显示出ViewModel相关的类型名称,同时带上了筛选按钮。
下面给你两种不用重写ToString()的解决方案,都是通过模板区分正常列头和填充列头:
方案一:在现有模板中添加触发器,动态隐藏填充列的按钮和文本
这种方案不需要拆分模板,只需要给你的ControlTemplate添加触发器,判断列头的Content类型,自动隐藏筛选按钮并清空文本:
<DataGrid ItemsSource="{Binding Customers}"> <DataGrid.ColumnHeaderStyle> <Style TargetType="{x:Type DataGridColumnHeader}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridColumnHeader}"> <Grid Margin="0,0,0,0" Background="Black"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="15"/> <ColumnDefinition Width="21"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock x:Name="HeaderText" Text="{Binding}" HorizontalAlignment="Right" Foreground="White" Grid.Column="0" TextTrimming="CharacterEllipsis"/> <ToggleButton x:Name="Filter" Grid.Column="2" Content="Filter" Height="20"/> <Thumb x:Name="PART_RightHeaderGripper" Grid.Column="3" HorizontalAlignment="Right" Width="2" BorderThickness="1" BorderBrush="{Binding VerticalGridLinesBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" Cursor="SizeWE"/> </Grid> <ControlTemplate.Triggers> <!-- 处理填充列的两种情况:Content为空或者是CollectionView实例 --> <Trigger Property="Content" Value="{x:Null}"> <Setter TargetName="Filter" Property="Visibility" Value="Collapsed"/> <Setter TargetName="HeaderText" Property="Text" Value=""/> </Trigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Content.GetType()}" Value="{x:Type CollectionView}"> <Setter TargetName="Filter" Property="Visibility" Value="Collapsed"/> <Setter TargetName="HeaderText" Property="Text" Value=""/> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </DataGrid.ColumnHeaderStyle> </DataGrid>
原理说明
触发器会自动识别填充列的表头:
- 如果列头的
Content是null,或者是CollectionView类型(也就是你的数据源视图),就会隐藏筛选按钮,并清空文本,只保留黑色背景和列头拖拽手柄。
方案二:拆分模板,给正常列和填充列分别设置模板
这种方案更清晰,把两种模板分开定义,通过触发器给不同列头应用对应模板:
<DataGrid ItemsSource="{Binding Customers}"> <DataGrid.Resources> <!-- 无按钮的纯黑模板,专门给填充列用 --> <ControlTemplate x:Key="EmptyHeaderTemplate" TargetType="{x:Type DataGridColumnHeader}"> <Grid Background="Black"> <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Width="2" BorderThickness="1" BorderBrush="{Binding VerticalGridLinesBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" Cursor="SizeWE"/> </Grid> </ControlTemplate> <!-- 带筛选按钮的模板,给正常列头用 --> <ControlTemplate x:Key="FilterHeaderTemplate" TargetType="{x:Type DataGridColumnHeader}"> <Grid Margin="0,0,0,0" Background="Black"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="15"/> <ColumnDefinition Width="21"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding}" HorizontalAlignment="Right" Foreground="White" Grid.Column="0" TextTrimming="CharacterEllipsis"/> <ToggleButton Grid.Column="2" Content="Filter" Height="20"/> <Thumb x:Name="PART_RightHeaderGripper" Grid.Column="3" HorizontalAlignment="Right" Width="2" BorderThickness="1" BorderBrush="{Binding VerticalGridLinesBrush, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" Cursor="SizeWE"/> </Grid> </ControlTemplate> </DataGrid.Resources> <DataGrid.ColumnHeaderStyle> <Style TargetType="{x:Type DataGridColumnHeader}"> <!-- 默认应用带筛选按钮的模板 --> <Setter Property="Template" Value="{StaticResource FilterHeaderTemplate}"/> <Style.Triggers> <!-- 填充列切换到无按钮模板 --> <Trigger Property="Content" Value="{x:Null}"> <Setter Property="Template" Value="{StaticResource EmptyHeaderTemplate}"/> </Trigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Content.GetType()}" Value="{x:Type CollectionView}"> <Setter Property="Template" Value="{StaticResource EmptyHeaderTemplate}"/> </DataTrigger> </Style.Triggers> </Style> </DataGrid.ColumnHeaderStyle> <!-- 如果是手动定义列,这里可以添加你的列,比如: <DataGrid.Columns> <DataGridTextColumn Header="CustomerName" Binding="{Binding Name}"/> <DataGridTextColumn Header="Age" Binding="{Binding Age}"/> </DataGrid.Columns> --> </DataGrid>
原理说明
- 我们先定义两个模板:一个是带筛选按钮的正常模板,一个是只有黑色背景和拖拽手柄的空模板。
- 默认情况下所有列头用正常模板,当识别到是填充列时,自动切换到空模板,完美实现你要的效果。
两种方案都能解决你的问题,不需要修改ViewModel的ToString()方法,推荐第二种方案,因为模板拆分后更易维护和扩展。
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

