Reputation: 69985
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 theContentTemplate
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.
Upvotes: 1
Views: 1179
Reputation: 616
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>
Upvotes: 1