How to customize and reuse a DataGridColumnHeader style?

instcode picture instcode · Mar 21, 2010 · Viewed 9.6k times · Source

I'm trying to customize the column headers of a DataGrid to show sub-column headers as in the following screenshot:

alt text

I've made a style for 2 sub-columns as in the following XAML:

<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
xmlns:sl="clr-namespace:UI" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="UI.ColumnHeaderGrid"
mc:Ignorable="d">
<UserControl.Resources>
    <Style x:Key="SplitColumnHeaderStyle" TargetType="primitives:DataGridColumnHeader">
        <Setter Property="Foreground" Value="#FF000000"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="IsTabStop" Value="False"/>
        <Setter Property="SeparatorBrush" Value="#FFC9CACA"/>
        <Setter Property="Padding" Value="4"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="primitives:DataGridColumnHeader">
                    <Grid x:Name="Root">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Rectangle x:Name="BackgroundRectangle" Fill="#FF1F3B53" Stretch="Fill" Grid.ColumnSpan="2"/>
                        <Rectangle x:Name="BackgroundGradient" Stretch="Fill" Grid.ColumnSpan="2">
                            <Rectangle.Fill>
                                <LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">
                                    <GradientStop Color="#FCFFFFFF" Offset="0.015"/>
                                    <GradientStop Color="#F7FFFFFF" Offset="0.375"/>
                                    <GradientStop Color="#E5FFFFFF" Offset="0.6"/>
                                    <GradientStop Color="#D1FFFFFF" Offset="1"/>
                                </LinearGradientBrush>
                            </Rectangle.Fill>
                        </Rectangle>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition Width="1"/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <TextBlock Grid.Row="0" Grid.ColumnSpan="3" Text="Headers" TextAlignment="Center"/>
                            <Rectangle Grid.Row="1" Grid.ColumnSpan="3" Fill="{TemplateBinding SeparatorBrush}" Height="1"/>
                            <TextBlock Grid.Row="2" Grid.Column="0" Text="Header 1" TextAlignment="Center"/>
                            <Rectangle Grid.Row="2" Grid.Column="1" Fill="{TemplateBinding SeparatorBrush}" Width="1"/>
                            <TextBlock Grid.Row="2" Grid.Column="2" Text="Header 2" TextAlignment="Center"/>
                            <Path x:Name="SortIcon" Grid.Column="2" Fill="#FF444444" Stretch="Uniform" HorizontalAlignment="Left" Margin="4,0,0,0" VerticalAlignment="Center" Width="8" Opacity="0" RenderTransformOrigin=".5,.5" Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "/>
                        </Grid>
                        <Rectangle x:Name="VerticalSeparator" Fill="{TemplateBinding SeparatorBrush}" VerticalAlignment="Stretch" Width="1" Visibility="{TemplateBinding SeparatorVisibility}" Grid.Column="1"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>
<data:DataGrid x:Name="LayoutRoot">
    <data:DataGrid.Columns>
        <data:DataGridTemplateColumn HeaderStyle="{StaticResource SplitColumnHeaderStyle}">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Border Grid.Column="0" BorderBrush="#FFC9CACA" BorderThickness="0,0,0,0">
                            <TextBlock Grid.Column="0" Text="{Binding GridData.Column1}"/>
                        </Border>
                        <Border Grid.Column="1" BorderBrush="#FFC9CACA" BorderThickness="1,0,0,0">
                            <TextBlock Grid.Column="0" Text="{Binding GridData.Column2}"/>
                        </Border>
                    </Grid>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

Now I want to reuse & extend this style to support 2->6 sub-column headers but I don't know if there is a way to do this, like ContentPresenter "overriding":

<Style x:Key="SplitColumnHeaderStyle" TargetType="primitives:DataGridColumnHeader">
    <Setter property="Template">
        <Setter.Value>
            ...
            <ContentPresenter Content="{TemplateBinding Content}".../>
            ...
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="TwoSubColumnHeaderStyle" BasedOn="SplitColumnHeaderStyle">
    <Setter property="Content">
        <Setter.Value>
            <Grid 2x2.../>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="ThreeSubColumnHeaderStyle" BasedOn="SplitColumnHeaderStyle">
    <Setter property="Content">
        <Setter.Value>
            <Grid 2x3.../>
        </Setter.Value>
    </Setter>
</Style>

Anyway, please help me on these issues:

  1. Given the templates above, how to support more sub-column headers without having to create new template for each?

  2. Assuming that the issue above is solved. How could I attach column names outside the styles?

  3. I see that some parts, properties & visualization rules in the XAML are just copies from the original Silverlight component's style, i.e. BackgroundGradient, BackgroundRectangle, VisualStateManager... They must be there in order to support default behaviors or effects but... does anyone know how to remove them, but keep all the default behaviors/effects?

Please be specific because I'm just getting started with C# & Silverlight.