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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:23:32