4
votes

I have created a custom style for my WPF datagrid by overriding its control template - nothing unusual, just copied the original template and modified it. Unfortunately, when the grid is drawn, the fully qualified class name of my ViewModel is showing up in the header (the ViewModel happens to be the DataContext of the UserControl that contains the DataGrid). Using Snoop, I have narrowed down which element in the template is showing this class name:

<DataGridColumnHeadersPresenter 
 Grid.Column="1" 
 Name="PART_ColumnHeadersPresenter"
 Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Column}}"/>

The visual tree for this part looks like this:

PART_ColumnHeadersPresenter (DataGridColumnHeadersPresenter)
    (Grid)
        headerBorder (DataGridHeaderBorder)
            (Border)
                (TextBlock)

It is this text block that contains the class name! So the questions are

  1. Why does the Border need a TextBlock?
  2. Why is the TextBlock initialized with the class name of the DataContext?
  3. Is there a property at the DataGrid level that controls the conent of this TextBlock?

P.S. To answer the comment below, I am specifying proper paths for the ItemSource and also for each column:

<DataGrid
    ItemsSource="{Binding Path=Orders, Mode=TwoWay}"
    AutoGenerateColumns="False"
    IsReadOnly="True">
    <DataGrid.Columns>
        <DataGridTextColumn
            Header="Creation Time"
            Binding="{Binding Path=CreationTime}"
            CellStyle="{StaticResource LeftAlignedCellStyle}"
            SortMemberPath="CreationTime">
        </DataGridTextColumn>
        ...
    </DataGrid.Columns>
</DataGrid>

I don't see any place where I must bind the TextBlock for the column header border. Don't even know how it makes sense!

Based on Avatar's comment, I am sharing my entire template. See below:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Brushes.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <!-- ColumnHeader Gripper Style -->
    <Style x:Key="ColumnHeaderGripperStyle" TargetType="{x:Type Thumb}">
        <Setter Property="Width" Value="8"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Cursor" Value="SizeWE"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Thumb}">
                    <Border Padding="{TemplateBinding Padding}"
                            Background="{TemplateBinding Background}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- DataGridColumnHeader Style -->
    <Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Background" Value="{StaticResource HeaderBackgroundBrush}"/>
        <Setter Property="Foreground" Value="{StaticResource HeaderForegroundBrush}"/>
        <Setter Property="BorderBrush" Value="{StaticResource HeaderBorderBrush}" />
        <Setter Property="BorderThickness" Value="0,1,0,1" />
        <Setter Property="FontFamily" Value="Trebuchet MS" />
        <Setter Property="FontSize" Value="12" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                    <Grid>
                        <themes:DataGridHeaderBorder
                            x:Name="headerBorder"
                            SortDirection="{TemplateBinding SortDirection}"
                            IsHovered="{TemplateBinding IsMouseOver}"
                            IsPressed="{TemplateBinding IsPressed}"
                            IsClickable="{TemplateBinding CanUserSort}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Padding ="{TemplateBinding Padding}"
                            SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
                            SeparatorBrush="{TemplateBinding SeparatorBrush}">
                            <Border BorderBrush="{StaticResource HeaderInnerBorderBrush}" 
                                    BorderThickness="0,1,0,0">
                                <TextBlock
                                    Text="{Binding}" Margin="7,0,7,0"
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                            </Border>
                        </themes:DataGridHeaderBorder>

                        <Thumb x:Name="PART_LeftHeaderGripper"
                               HorizontalAlignment="Left"
                               Style="{StaticResource ColumnHeaderGripperStyle}"/>
                        <Thumb x:Name="PART_RightHeaderGripper"
                               HorizontalAlignment="Right"
                               Style="{StaticResource ColumnHeaderGripperStyle}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="headerBorder" Property="Background" 
                                    Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="headerBorder" Property="Background" 
                                    Value="{StaticResource HeaderPressedBackgroundBrush}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- Right Aligned DataGridColumnHeader Style-->
    <Style x:Key="RightAlignedColumnHeaderStyle" 
           TargetType="{x:Type DataGridColumnHeader}"
           BasedOn="{StaticResource DataGridColumnHeaderStyle}">
        <Setter Property="HorizontalContentAlignment" Value="Right"/>
    </Style>

    <!-- Center Aligned DataGridColumnHeader Style-->
    <Style x:Key="CenterAlignedColumnHeaderStyle" 
           TargetType="{x:Type DataGridColumnHeader}"
           BasedOn="{StaticResource DataGridColumnHeaderStyle}">
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
    </Style>

    <!-- DataGridRowHeader Gripper -->
    <Style x:Key="RowHeaderGripperStyle" TargetType="{x:Type Thumb}">
        <Setter Property="Height" Value="8"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Cursor" Value="SizeNS"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Thumb}">
                    <Border Padding="{TemplateBinding Padding}"
                            Background="{TemplateBinding Background}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- DataGridRowHeader Style -->
    <Style x:Key="{x:Type DataGridRowHeader}"
           TargetType="{x:Type DataGridRowHeader}">
        <Setter Property="Background" Value="{StaticResource HeaderBackgroundBrush}" />
        <Setter Property="BorderBrush" Value="{StaticResource HeaderBorderBrush}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridRowHeader}">
                    <Grid>
                        <themes:DataGridHeaderBorder 
                            x:Name="headerBorder"
                            IsSelected="{TemplateBinding IsRowSelected}"
                            IsHovered ="{TemplateBinding IsMouseOver}"
                            IsPressed="{TemplateBinding IsPressed}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="1,0,1,1"
                            Padding ="{TemplateBinding Padding}"
                            Orientation="Horizontal"
                            SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
                            SeparatorBrush="{TemplateBinding SeparatorBrush}">
                            <Border BorderBrush="{StaticResource HeaderInnerBorderBrush}"
                                    BorderThickness="0,1,0,0">
                                <StackPanel Orientation="Horizontal">
                                    <ContentPresenter
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                        VerticalAlignment="Center"/>
                                    <Control
                                        SnapsToDevicePixels="false"
                                        Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}, Path=(Validation.HasError), Converter={StaticResource bool2VisibilityConverter}}"
                                        Template="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}, Path=ValidationErrorTemplate}" />
                                </StackPanel>
                            </Border>
                        </themes:DataGridHeaderBorder>

                        <Thumb x:Name="PART_TopHeaderGripper"
                               VerticalAlignment="Top"
                               Style="{StaticResource RowHeaderGripperStyle}"/>
                        <Thumb x:Name="PART_BottomHeaderGripper"
                               VerticalAlignment="Bottom"
                               Style="{StaticResource RowHeaderGripperStyle}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="headerBorder" Property="Background" 
                                    Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="headerBorder" Property="Background" 
                                    Value="{StaticResource HeaderPressedBackgroundBrush}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- DataGridElement Styles -->
    <Style x:Key="DataGridElementStyle" TargetType="{x:Type FrameworkElement}">
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Margin" Value="7 0 7 0" />
    </Style>
    <Style x:Key="LeftAlignedElementStyle" TargetType="{x:Type FrameworkElement}" BasedOn="{StaticResource DataGridElementStyle}">
        <Setter Property="HorizontalAlignment" Value="Left" />
    </Style>
    <Style x:Key="CenterAlignedElementStyle" TargetType="{x:Type FrameworkElement}" BasedOn="{StaticResource DataGridElementStyle}">
        <Setter Property="HorizontalAlignment" Value="Center" />
    </Style>
    <Style x:Key="RightAlignedElementStyle" TargetType="{x:Type FrameworkElement}" BasedOn="{StaticResource DataGridElementStyle}">
        <Setter Property="HorizontalAlignment" Value="Right" />
    </Style>

    <!-- DataGridCell Styles -->
    <Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}">
        <!-- Remove blue highlight when cell is selected -->
        <Setter Property="Background" Value="Transparent" />
        <!-- Don't change text color when cell is selected -->
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=Foreground}"/>
            </Trigger>
        </Style.Triggers>
    </Style>

    <Style x:Key="LeftAlignedCellStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridCellStyle}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridCell}">
                    <Grid Background="{TemplateBinding Background}">
                        <ContentPresenter Style="{StaticResource LeftAlignedElementStyle}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- Center Aligned DataGridCell Style -->
    <Style x:Key="CenterAlignedCellStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridCellStyle}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridCell}">
                    <Grid Background="{TemplateBinding Background}">
                        <ContentPresenter Style="{StaticResource CenterAlignedElementStyle}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- Right Aligned DataGridCell Style -->
    <Style x:Key="RightAlignedCellStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DataGridCellStyle}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGridCell}">
                    <Grid Background="{TemplateBinding Background}">
                        <ContentPresenter Style="{StaticResource RightAlignedElementStyle}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!-- SelectAllButton ControlTemplate -->
    <ControlTemplate x:Key="SelectAllButtonTemplate" TargetType="{x:Type Button}">
        <Grid>
            <Rectangle x:Name="Border" SnapsToDevicePixels="True"
                       Stroke="{StaticResource HeaderBorderBrush}"
                       Fill="{StaticResource HeaderBackgroundBrush}" />
            <Border SnapsToDevicePixels="True" Margin="1,1,1,0"
                    BorderBrush="White" BorderThickness="0,1,0,0" />
            <Polygon x:Name="Arrow"
                     HorizontalAlignment="Right"
                     VerticalAlignment="Bottom"
                     Margin="8,8,3,3"
                     Opacity="0.15"
                     Fill="Black"
                     Stretch="Uniform"
                     Points="0,10 10,10 10,0" />
        </Grid>
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter TargetName="Border" Property="Fill"
                        Value="{StaticResource HeaderHighlightedBackgoundBrush}" />
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter TargetName="Border" Property="Fill"
                        Value="{StaticResource HeaderPressedBackgroundBrush}" />
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter TargetName="Arrow" Property="Visibility" Value="Collapsed" />
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

    <!-- DataGrid Style -->
    <Style x:Key="{x:Type DataGrid}" TargetType="{x:Type DataGrid}">
        <Setter Property="Background" Value="{StaticResource DefaultControlBackgroundBrush}"/>
        <Setter Property="Foreground" Value="{StaticResource DefaultControlForegroundBrush}"/>
        <!-- Remove border around the grid -->
        <Setter Property="BorderBrush" Value="{x:Null}" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="HorizontalGridLinesBrush" Value="{StaticResource GridLineColorBrush}" />
        <Setter Property="VerticalGridLinesBrush" Value="{StaticResource GridLineColorBrush}" />
        <Setter Property="AlternatingRowBackground" Value="{StaticResource AlternateRowBackgroundBrush}" />
        <Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGridColumnHeaderStyle}"/>
        <!-- This is needed to force DG to have a non-default value.  Otherwise the DGR.DetailsVisibility cannot have a value of VisibleWhenSelected by default. -->
        <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected" />
        <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
        <!-- Turn off row headers by default. -->
        <Setter Property="HeadersVisibility" Value="Column" />
        <Setter Property="GridLinesVisibility" Value="Horizontal" />
        <Setter Property="ColumnHeaderHeight" Value="32" />
        <Setter Property="RowHeight" Value="32" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGrid}">
                    <Border
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        SnapsToDevicePixels="True"
                        Padding="{TemplateBinding Padding}">
                        <ScrollViewer Focusable="false" Name="DG_ScrollViewer">
                            <ScrollViewer.Template>
                                <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto"/>
                                            <RowDefinition Height="*"/>
                                            <RowDefinition Height="Auto"/>
                                        </Grid.RowDefinitions>

                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto"/>
                                            <ColumnDefinition Width="*"/>
                                            <ColumnDefinition Width="Auto"/>
                                        </Grid.ColumnDefinitions>

                                        <!--Left Column Header Corner -->
                                        <Button 
                                            Command="{x:Static DataGrid.SelectAllCommand}"
                                            Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=CellsPanelHorizontalOffset}"
                                            Focusable="false"
                                            Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.All}}" 
                                            Template="{StaticResource SelectAllButtonTemplate}"/>
                                        <!--Column Headers-->
                                        <DataGridColumnHeadersPresenter 
                                            Grid.Column="1" 
                                            Name="PART_ColumnHeadersPresenter"
                                            Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Column}}"/>

                                        <!--DataGrid content-->
                                        <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" />

                                        <ScrollBar
                                            Grid.Row="0" Grid.RowSpan="2" Grid.Column="2" Name="PART_VerticalScrollBar"
                                            Orientation="Vertical"
                                            Maximum="{TemplateBinding ScrollableHeight}"
                                            ViewportSize="{TemplateBinding ViewportHeight}"
                                            Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
                                            Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>

                                        <Grid Grid.Row="2" Grid.Column="1">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/>
                                                <ColumnDefinition Width="*"/>
                                            </Grid.ColumnDefinitions>
                                            <ScrollBar 
                                                Grid.Column="1"
                                                Name="PART_HorizontalScrollBar"
                                                Orientation="Horizontal"
                                                Maximum="{TemplateBinding ScrollableWidth}"
                                                ViewportSize="{TemplateBinding ViewportWidth}"
                                                Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
                                                Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                                        </Grid>
                                    </Grid>
                                </ControlTemplate>
                            </ScrollViewer.Template>
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </ScrollViewer>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsGrouping" Value="true">
                <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
            </Trigger>
        </Style.Triggers>
    </Style>

</ResourceDictionary>
1
Yo do know that the default .ToString() gives the classname, right? So maybe you are missing a Path=... or something.Henk Holterman
I have updated my question to clarify your comment.Naresh
Hi naresh, Could you please share the edited template as well. 1) By Implementation the DataGridColumnHeaderPresenter is derived from Border since it is a content control, you'll be able to assign any element as a child. In this case, it is a TextBlock. 2) That - we have to check the template fully. Then, only we'll be able to come to the conclusion. 3) Yes, the Header Property of the DataGridTextColumn. Which you are already assigning. msdn.microsoft.com/en-us/library/…Prince Ashitaka
Hi Avatar, I have added my entire template to the question. Please review and let me know if you find something unusual. FYI, the columns in my example are showing correctly. For example, the "Creation Time" column in the example above is showing correctly. It's just that there is a text block sitting under these columns and since I have only a few columns, the tail end of the fully classified class name shows through (since it is larger than the total width of the real columns)!Naresh

1 Answers

7
votes

The default template for DataGridColumnHeadersPresenter looks something like this:

<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
    <Grid>
        <DataGridColumnHeader IsHitTestVisible="False"
            Name="PART_FillerColumnHeader"/>
        <ItemsPresenter />
    </Grid>
</ControlTemplate>

The ItemsPresenter will create a DataGridColumnHeader for each column, but the template also includes a single DataGridColumnHeader that stretches across the entire grid to act as a background. It has no content, so it normally just draws the border in the appropriate theme.

However, your DataGridColumnHeader template includes a TextBlock instead of a ContentPresenter, so it will render the DataContext as a string whether or not it is also the content. Try using a ContentPresenter instead of a TextBlock:

<Border BorderBrush="{StaticResource HeaderInnerBorderBrush}" 
        BorderThickness="0,1,0,0">
    <ContentPresenter
        Margin="7,0,7,0"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Border>