1

In short, the question title says it all. For those that want more detail, here is the crux of my problem: I need to apply a custom ControlTemplate to the DataGridColumnHeader elements in my DataGrid control, but I also need to style them differently, depending on the cell data nearest the header. However, when I set both the ContentTemplateSelector and Template properties on a DataGridColumnHeader element, the DataTemplateSelector that is set as the value of the ContentTemplateSelector property is not called. Commenting out the Template property setting confirms this to be the case, as the DataTemplateSelector element will now be called.

Yes, I know that you guys love to see some code, but I have completely templated the whole DataGrid control to look like Excel, so as you can imagine, I have far too much code to display here. But just to please you code hungry devs, I've recreated my problem in a much simpler example... let's first see the XAML:

<Window x:Class="WpfApp1.MainWindow"
    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:Local="clr-namespace:WpfApp1"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid>
            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
            <DataGrid.Items>
                <System:String>One</System:String>
                <System:String>Two</System:String>
                <System:String>Three</System:String>
            </DataGrid.Items>
            <DataGrid.Resources>
                <Local:StringDataTemplateSelector x:Key="StringDataTemplateSelector" />
                <Style TargetType="{x:Type DataGridColumnHeader}" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
                    <Setter Property="ContentTemplateSelector" Value="{StaticResource StringDataTemplateSelector}" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                                <Grid>
                                    <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" />
                                    <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" />
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </DataGrid.Resources>
        </DataGrid>
    </Grid>
</Window>

Now the most simple DataTemplateSelector class:

using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    public class StringDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            Debugger.Break();
            return null;
        }
    }
}

In the XAML, we see a DataGrid, with just one DataGridTemplateColumn and three string values, one on each row, and some resources. There is a Style for the DataGridColumnHeader element in the Resource section, with the most simple ControlTemplate set up for it, that only includes the required named parts from the default ControlTemplate.

If you run the application as it is, then it will NOT currently break at the Debugger.Break() method in the StringDataTemplateSelector class. This is unexpected. If you now comment out the setting of the Template property in the Style and run the application again, then you will now see that program execution will now break at the Debugger.Break() method, as expected.

Further information:

In the Remarks section of the ContentControl.ContentTemplateSelector Property page of MSDN, it states that

If both the ContentTemplateSelector and the ContentTemplate properties are set, then this property is ignored.

However, it does not mention the Template property and there is also no mention of this on the Control.Template Property page on MSDN.

Furthermore, I tried this same setup using a simple Button control and can confirm that setting both the ContentTemplateSelector and the ContentTemplate properties on that does NOT stop the StringDataTemplateSelector class from being called:

<ItemsControl>
    <ItemsControl.Resources>
        <Local:StringDataTemplateSelector x:Key="StringDataTemplateSelector" />
        <Style TargetType="{x:Type Button}">
            <Setter Property="ContentTemplateSelector" Value="{StaticResource StringDataTemplateSelector}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid>
                            <Ellipse Stroke="Red" StrokeThickness="1" Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding Height}" />
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ItemsControl.Resources>
    <Button Content="One" />
    <Button Content="Two" />
    <Button Content="Three" />
</ItemsControl>

So, what I'm after is a way to apply a custom ControlTemplate element to the DataGridColumnHeader objects, yet still be able to have the DataTemplateSelector class called during the rendering process.

Sheridan
  • 68,826
  • 24
  • 143
  • 183

1 Answers1

1

add a content presenter in your controltemplate?

<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
    <Grid>
           <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" />
           <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" />
           <ContentPresenter></ContentPresenter>
    </Grid>
</ControlTemplate>
Milan
  • 616
  • 5
  • 11
  • Thanks, but as I said, I have minimised my code to display here. I have no problem displaying the data. My problem is that the `DataTemplateSelector` will not run when the `Template` property is also set. – Sheridan Jan 29 '18 at 16:17
  • i get inside DataTemplateSelector once i add the content presenter in your Template, you do not? – Milan Jan 29 '18 at 16:22
  • Hmmm... that's interesting. But in my actual `ControlTemplate`, I *do* have a `ContentPresenter`, and that does not make the `DataTemplateSelector` run. – Sheridan Jan 29 '18 at 16:50
  • apparently, the default controltemplate for datagridcolumnheader is exactly what i posted above, just that ContentPresenter is inside a DataGridHeaderBorder, the thing with that hardcoded sorting behavior. it makes no sense to me that the default template works fine, but using a pretty much same template doesnt – Milan Jan 29 '18 at 19:20
  • Yes, it doesn't make much sense to me either. I also have the `DataGridHeaderBorder` element from the default `ControlTemplate` in my template... in fact, it *is* the default `ControlTemplate`, but just with an extra element. I'll see if I can work out a better example. – Sheridan Jan 30 '18 at 08:31
  • did you perhaps also edit the DataGrid.Template and remove the DataGridColumnHeadersPresenter in some situations? or made it invisible depending on something? – Milan Jan 30 '18 at 10:02
  • No, every part of the original `ControlTemplate` is definitely still there. All I did was add additional elements here and there. – Sheridan Jan 30 '18 at 11:40
  • did you find a solution mayhaps? or a way to reproduce it in a new example? – Milan Feb 07 '18 at 10:24
  • No sorry, I couldn't reproduce the problem any better, as I've had to work on other projects. It's still a problem and I'll try to get back to this when I can. +1 for your help so far though. ;) – Sheridan Feb 07 '18 at 11:20