Halil Ibrahim
Halil Ibrahim

Reputation: 1369

Silverlight DataGrid control customizing column headers

I want to improve default datagrid control by some needs like filtering, easy adding-editing etc... I'm trying to customize column headers but couldn't get it. I need to add some other controls to headers and reach them in code side. Is there a way to do this?

Thanks.

<sdk:DataGrid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
mc:Ignorable="d"
x:Class="DataGridTest.SLDataGrid"
d:DesignWidth="640" d:DesignHeight="480">
<sdk:DataGrid.Resources>
    <Style x:Key="SLDataGridStyle" TargetType="sdk:DataGrid">
        <Setter Property="RowBackground" Value="#AAEAEFF4"/>
        <Setter Property="AlternatingRowBackground" Value="#00FFFFFF"/>
        <Setter Property="Background" Value="#FFFFFFFF"/>
        <Setter Property="HeadersVisibility" Value="Column"/>
        <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
        <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
        <Setter Property="SelectionMode" Value="Extended"/>
        <Setter Property="CanUserReorderColumns" Value="True"/>
        <Setter Property="CanUserResizeColumns" Value="True"/>
        <Setter Property="CanUserSortColumns" Value="True"/>
        <Setter Property="AutoGenerateColumns" Value="True"/>
        <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected"/>
        <Setter Property="BorderBrush">
            <Setter.Value>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FFA3AEB9" Offset="0"/>
                    <GradientStop Color="#FF8399A9" Offset="0.375"/>
                    <GradientStop Color="#FF718597" Offset="0.375"/>
                    <GradientStop Color="#FF617584" Offset="1"/>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="DragIndicatorStyle">
            <Setter.Value>
                <Style TargetType="ContentControl">
                    <Setter Property="Foreground" Value="#7FFFFFFF"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ContentControl">
                                <Grid>
                                    <VisualStateManager.VisualStateGroups>
                                        <VisualStateGroup x:Name="SortStates">
                                            <VisualState x:Name="Unsorted"/>
                                            <VisualState x:Name="SortAscending">
                                                <Storyboard>
                                                    <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SortIcon"/>
                                                </Storyboard>
                                            </VisualState>
                                            <VisualState x:Name="SortDescending">
                                                <Storyboard>
                                                    <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SortIcon"/>
                                                    <DoubleAnimation Duration="0" To="-.9" Storyboard.TargetProperty="(RenderTransform).ScaleY" Storyboard.TargetName="SortIcon"/>
                                                </Storyboard>
                                            </VisualState>
                                        </VisualStateGroup>
                                    </VisualStateManager.VisualStateGroups>
                                    <Rectangle x:Name="BackgroundRectangle" Grid.ColumnSpan="2" Fill="#66808080" Stretch="Fill"/>
                                    <Rectangle x:Name="BackgroundGradient" Grid.ColumnSpan="2" Opacity="0" Stretch="Fill">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">
                                                <GradientStop Color="#FFFFFFFF" Offset="0.015"/>
                                                <GradientStop Color="#F9FFFFFF" Offset="0.375"/>
                                                <GradientStop Color="#E5FFFFFF" Offset="0.6"/>
                                                <GradientStop Color="#C6FFFFFF" Offset="1"/>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                    <Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition/>
                                            <ColumnDefinition Width="Auto"/>
                                        </Grid.ColumnDefinitions>
                                        <ContentPresenter Content="{TemplateBinding Content}"/>
                                        <Path x:Name="SortIcon" Grid.Column="1" Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z " Fill="#7FFFFFFF" HorizontalAlignment="Left" Margin="4,0,0,0" Opacity="0" RenderTransformOrigin=".5,.5" Stretch="Uniform" VerticalAlignment="Center" Width="8">
                                            <Path.RenderTransform>
                                                <ScaleTransform ScaleY=".9" ScaleX=".9"/>
                                            </Path.RenderTransform>
                                        </Path>
                                    </Grid>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="DropLocationIndicatorStyle">
            <Setter.Value>
                <Style TargetType="ContentControl">
                    <Setter Property="Background" Value="#FF3F4346"/>
                    <Setter Property="Width" Value="2"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ContentControl">
                                <Rectangle Fill="{TemplateBinding Background}" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="GridLinesVisibility" Value="Vertical"/>
        <Setter Property="HorizontalGridLinesBrush" Value="#FFC9CACA"/>
        <Setter Property="IsTabStop" Value="True"/>
        <Setter Property="VerticalGridLinesBrush" Value="#FFC9CACA"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="sdk:DataGrid">
                    <Grid>
                        <VisualStateManager.CustomVisualStateManager>
                            <ei:ExtendedVisualStateManager/>
                        </VisualStateManager.CustomVisualStateManager>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates" ei:ExtendedVisualStateManager.UseFluidLayout="True">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition GeneratedDuration="0:0:0.3"/>
                                </VisualStateGroup.Transitions>
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="ValidationStates" ei:ExtendedVisualStateManager.UseFluidLayout="True">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition GeneratedDuration="0:0:0.3"/>
                                </VisualStateGroup.Transitions>
                                <VisualState x:Name="Invalid"/>
                                <VisualState x:Name="Valid"/>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2">
                            <Grid x:Name="Root" Background="{TemplateBinding Background}">
                                <Grid.Resources>
                                    <ControlTemplate x:Key="TopLeftHeaderTemplate" TargetType="sdk:DataGridColumnHeader">
                                        <Grid x:Name="Root">
                                            <Grid.RowDefinitions>
                                                <RowDefinition/>
                                                <RowDefinition/>
                                                <RowDefinition Height="Auto"/>
                                            </Grid.RowDefinitions>
                                            <Border BorderBrush="#FFC9CACA" BorderThickness="0,0,1,0" Background="#FF1F3B53" Grid.RowSpan="2">
                                                <Rectangle Stretch="Fill" StrokeThickness="1">
                                                    <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>
                                            </Border>
                                            <Rectangle Fill="#FFDBDCDC" Height="1" Grid.RowSpan="2" StrokeThickness="1" VerticalAlignment="Bottom" Width="Auto"/>
                                        </Grid>
                                    </ControlTemplate>
                                    <ControlTemplate x:Key="TopRightHeaderTemplate" TargetType="sdk:DataGridColumnHeader">
                                        <Grid x:Name="RootElement">
                                            <Grid.RowDefinitions>
                                                <RowDefinition/>
                                                <RowDefinition/>
                                                <RowDefinition Height="Auto"/>
                                            </Grid.RowDefinitions>
                                            <Border BorderBrush="#FFC9CACA" BorderThickness="1,0,0,0" Background="#FF1F3B53" Grid.RowSpan="2">
                                                <Rectangle Stretch="Fill">
                                                    <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>
                                            </Border>
                                        </Grid>
                                    </ControlTemplate>
                                </Grid.Resources>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition/>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="Auto"/>
                                </Grid.RowDefinitions>
                                <sdk:DataGridColumnHeader x:Name="TopLeftCornerHeader" Template="{StaticResource TopLeftHeaderTemplate}" Width="22"/>
                                <sdk:DataGridColumnHeadersPresenter x:Name="ColumnHeadersPresenter" Grid.Column="1"/>
                                <sdk:DataGridColumnHeader x:Name="TopRightCornerHeader" Grid.Column="2" Template="{StaticResource TopRightHeaderTemplate}"/>
                                <Rectangle x:Name="ColumnHeadersAndRowsSeparator" Grid.ColumnSpan="3" Fill="#FFC9CACA" Height="1" StrokeThickness="1" VerticalAlignment="Bottom" Width="Auto"/>
                                <sdk:DataGridRowsPresenter x:Name="RowsPresenter" Grid.ColumnSpan="2" Grid.Row="1"/>
                                <Rectangle x:Name="BottomRightCorner" Grid.Column="2" Fill="#FFE9EEF4" Grid.Row="2"/>
                                <Rectangle x:Name="BottomLeftCorner" Grid.ColumnSpan="2" Fill="#FFE9EEF4" Grid.Row="2"/>
                                <ScrollBar x:Name="VerticalScrollbar" Grid.Column="2" Margin="0,-1,-1,-1" Orientation="Vertical" Grid.Row="1" Width="18"/>
                                <Grid Grid.Column="1" Grid.Row="2">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition/>
                                    </Grid.ColumnDefinitions>
                                    <Rectangle x:Name="FrozenColumnScrollBarSpacer"/>
                                    <ScrollBar x:Name="HorizontalScrollbar" Grid.Column="1" Height="18" Margin="-1,0,-1,-1" Orientation="Horizontal"/>
                                </Grid>
                                <sdk:ValidationSummary x:Name="ValidationSummary" Grid.ColumnSpan="3" MaxHeight="90" Grid.Row="3"/>
                            </Grid>
                        </Border>
                        <Border x:Name="DisabledVisualElement" Background="#8CFFFFFF" CornerRadius="2" HorizontalAlignment="Stretch" Height="Auto" IsHitTestVisible="False" Opacity="0" VerticalAlignment="Stretch" Width="Auto"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="SLDataGridColumnHeaderStyle" TargetType="sdk:DataGridColumnHeader">
        <Setter Property="Foreground" Value="#FF000000"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <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="sdk:DataGridColumnHeader">
                    <StackPanel x:Name="Root">
                        <VisualStateManager.CustomVisualStateManager>
                            <ei:ExtendedVisualStateManager/>
                        </VisualStateManager.CustomVisualStateManager>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates" ei:ExtendedVisualStateManager.UseFluidLayout="True">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition GeneratedDuration="0:0:0.3"/>
                                </VisualStateGroup.Transitions>
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <ColorAnimation Duration="0" To="#FF448DCA" Storyboard.TargetProperty="(Fill).Color" Storyboard.TargetName="BackgroundRectangle"/>
                                        <ColorAnimation Duration="0" To="#7FFFFFFF" Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" Storyboard.TargetName="BackgroundGradient"/>
                                        <ColorAnimation Duration="0" To="#CCFFFFFF" Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" Storyboard.TargetName="BackgroundGradient"/>
                                        <ColorAnimation Duration="0" To="#F2FFFFFF" Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" Storyboard.TargetName="BackgroundGradient"/>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ColorAnimation Duration="0" To="#FF448DCA" Storyboard.TargetProperty="(Fill).Color" Storyboard.TargetName="BackgroundRectangle"/>
                                        <ColorAnimation Duration="0" To="#D8FFFFFF" Storyboard.TargetProperty="(Fill).(GradientStops)[0].Color" Storyboard.TargetName="BackgroundGradient"/>
                                        <ColorAnimation Duration="0" To="#C6FFFFFF" Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" Storyboard.TargetName="BackgroundGradient"/>
                                        <ColorAnimation Duration="0" To="#8CFFFFFF" Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" Storyboard.TargetName="BackgroundGradient"/>
                                        <ColorAnimation Duration="0" To="#3FFFFFFF" Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" Storyboard.TargetName="BackgroundGradient"/>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="SortStates" ei:ExtendedVisualStateManager.UseFluidLayout="True">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition GeneratedDuration="0:0:0.3"/>
                                </VisualStateGroup.Transitions>
                                <VisualState x:Name="Unsorted"/>
                                <VisualState x:Name="SortAscending">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SortIcon"/>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="SortDescending">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="SortIcon"/>
                                        <DoubleAnimation Duration="0" To="-.9" Storyboard.TargetProperty="(RenderTransform).ScaleY" Storyboard.TargetName="SortIcon"/>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid Height="24" Width="147">
                            <Rectangle x:Name="BackgroundRectangle" Fill="#FF1F3B53" Stretch="Fill" Margin="0,0,-1,0"/>
                            <Rectangle x:Name="BackgroundGradient" Stretch="Fill" Margin="0,0,-1,0">
                                <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 HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="4,4,0,4" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <ContentPresenter Content="{TemplateBinding Content}"/>
                                <Path x:Name="SortIcon" Grid.Column="1" Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z " Fill="#FF444444" HorizontalAlignment="Left" Margin="4,0,0,0" Opacity="0" RenderTransformOrigin=".5,.5" Stretch="Uniform" VerticalAlignment="Center" Width="8">
                                    <Path.RenderTransform>
                                        <ScaleTransform ScaleY=".9" ScaleX=".9"/>
                                    </Path.RenderTransform>
                                </Path>
                            </Grid>
                            <Rectangle x:Name="VerticalSeparator" Fill="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}" VerticalAlignment="Stretch" Width="1" HorizontalAlignment="Right" Margin="0,0,-1,0" d:LayoutOverrides="Width"/>
                        </Grid>
                        <Grid Height="24" Background="#FFE4E4E4">
                            <TextBox TextWrapping="Wrap" Text="TextBox" d:LayoutOverrides="Width"/>
                        </Grid>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</sdk:DataGrid.Resources>
<sdk:DataGrid.Style>
    <StaticResource ResourceKey="SLDataGridStyle"/>
</sdk:DataGrid.Style>
<sdk:DataGrid.ColumnHeaderStyle>
    <StaticResource ResourceKey="SLDataGridColumnHeaderStyle"/>
</sdk:DataGrid.ColumnHeaderStyle>

Upvotes: 2

Views: 6822

Answers (3)

Edward
Edward

Reputation: 8596

Luke's answer is excellent.

However, I found that when I put a ComboBox into the header template, the binding (set in code-behind from part (2) of Luke's answer) disappeared after an item in the Combo was selected, or when the column is resized. This is because the column header is being recreated when the column resizes, so the binding gets lost.

I fixed it by setting the binding instead like this -

XAML -

       <DataTemplate>
            <StackPanel Orientation="Horizontal" Loaded="HeaderTemplate_OnLoaded" >
                <CheckBox IsChecked="{Binding Path=SomeFlag, Mode=TwoWay}" />
                <TextBlock Text="Header text" />
            </StackPanel>
       </DataTemplate>

Code-behind -

    private void HeaderTemplate_OnLoaded(object sender, RoutedEventArgs e)
    {
        ((FrameworkElement)sender).DataContext = this.DataContext;
    }

This worked for me without problems every time.

Upvotes: 3

Luke Woodward
Luke Woodward

Reputation: 64959

It is possible to add custom controls to the headers of a Silverlight DataGrid, but it's not obvious how to do so, and it is a bit of a pain.

There are two things you need to do:

  1. Specify the controls you want to add to the column header by applying a ContentTemplate to the HeaderStyle of the column header. For this to work, you'll need the following two XAML namespace declarations:

    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:dataprimitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
    
    <sdk:DataGridTemplateColumn Binding="...">
        <sdk:DataGridTemplateColumn.HeaderStyle>
            <Style TargetType="dataprimitives:DataGridColumnHeader">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <CheckBox IsChecked="{Binding Path=SomeFlag, Mode=TwoWay}" />
                                <TextBlock Text="Header text" />
                            </StackPanel>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </sdk:DataGridTemplateColumn.HeaderStyle>
        <!-- other DataGridTemplateColumn stuff -->
    <sdk:DataGridTemplateColumn>
    

    In this example, I've used a CheckBox with an IsChecked property bound to a view-model property SomeFlag, and a TextBlock. I used a DataGridTemplateColumn in this example, but this approach should also work with DataGridTextColumn and DataGridCheckBoxColumn provided you replace sdk:DataGridTemplateColumn.HeaderStyle with sdk:DataGridTextColumn.HeaderStyle and similarly for DataGridCheckBoxColumn.

  2. If you only want to add static content to the column header, there's nothing more you need to do. However, if you wish to use data-binding, you need a further step, as otherwise the data-context of the controls in the header will not be set correctly. This requires a bit of code-behind.

    The following methods search through the visual tree for ContentPresenters within DataGridColumnHeaders and set the DataContexts of these ContentPresenters:

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Media;
    
    /// <summary>
    /// Sets the DataContext of each column header within the given data-grid.
    /// </summary>
    /// <param name="dataGrid">The DataGrid.</param>
    /// <param name="dataContext">The data-context to set.</param>
    public static void SetHeaderDataContexts(DataGrid dataGrid, object dataContext)
    {
        // First we look for a DataGridColumnHeadersPresenter.
        DataGridColumnHeadersPresenter colsPresenter = GetObjectOfType<DataGridColumnHeadersPresenter>(dataGrid);
        if (colsPresenter == null)
        {
            return;
        }
    
        // The ColumnHeadersPresenter contains all of the column headers as
        // direct children.  Within each column header is a ContentPresenter,
        // whose DataContext will normally be null.  For each ContentPresenter
        // found, set its DataContext to be that given.
        int colHeaderCount = VisualTreeHelper.GetChildrenCount(colsPresenter);
        for (int i = 0; i < colHeaderCount; ++i)
        {
            var header = VisualTreeHelper.GetChild(colsPresenter, i) as DataGridColumnHeader;
            if (header != null)
            {
                ContentPresenter contentPresenter = GetObjectOfType<ContentPresenter>(header);
                if (contentPresenter != null)
                {
                    contentPresenter.DataContext = dataContext;
                }
            }
        }
    }
    
    /// <summary>
    /// Returns the first descendant object of the given parent object within
    /// the visual tree that is an instance of the specified type.
    /// </summary>
    /// <remarks>
    /// The visual tree is searched in a depth-first manner, and the first
    /// object of type <c>TObject</c> found within the tree is returned.  If no
    /// control of this type is found, <c>null</c> is returned instead.
    /// </remarks>
    /// <typeparam name="TObject">The type of object to search for.  This must
    /// be a subclass of <see cref="DependencyObject"/>.</typeparam>
    /// <param name="parent">The parent control to search within.</param>
    /// <returns>
    /// The first control of the specified type found, or <c>null</c> if none
    /// were found.
    /// </returns>
    public static TObject GetObjectOfType<TObject>(DependencyObject parent) where TObject : DependencyObject
    {
        int count = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < count; ++i)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            if (child is TObject)
            {
                return child as TObject;
            }
            else
            {
                TObject obj = GetObjectOfType<TObject>(child);
                if (obj != null)
                {
                    return obj;
                }
            }
        }
    
        return null;
    }
    

    The only thing that remains to be done is to call SetHeaderDataContexts. I found that calling it from the user-control constructor didn't work, nor did calling it from the Loaded event (in both cases it had no effect). Calling it from the LayoutUpdated event of the grid really didn't work, as that caused an exception to be thrown.

    However, calling it within the constructor by using

        Dispatcher.BeginInvoke(() => SetHeaderDataContexts(theDataGrid, this.DataContext));
    

    did appear to work.

Acknowledgements: the idea of setting the ContentTemplate comes from Lars Holm Jensen's answer to a similar question, and the idea of searching through the visual tree comes from a blog post from Manas Patnaik. Both links were found via a similar question.

Upvotes: 5

Yann Duran
Yann Duran

Reputation: 3879

The column header's style can indeed be changed. I was trying to center the header text & this link's technique worked for me: Silverlight DataGrid Header Horizontal Alignment

Upvotes: 0

Related Questions