I would like to create a custom DataGrid
column (or a DataGridCell
style), ideally in XAML, and use it multiple times without any code duplication. It should contain multiple different controls, each accessing a different property of the data object.
There seem to be two ways of doing this:
DataGridTemplateColumn
with a customCellTemplate
(see an example here)- custom
DataGridCell
style (something like this or this)
Both of these solutions suffer from the same issue. When specifying the bindings for the internal controls, they always seem to be related to the whole DataGrid
row rather than the cell.
DataGridTemplateColumn
does not have a Binding
attribute, so its CellTemplate
is bound to the whole row, making it impossible to reuse the same template for multiple columns, each accessing a different property of the row object.
Similarly, I can access the content of DataGridTextColumn
in a style that overrides the Template
via {TemplateBinding Content}
(see here), but I cannot find a way to access the content's properties, so I assume that is not possible.
Here is a dummy example of such DataGrid
to illustrate my situation:
public class Name
{
public string First { get; set; }
public string Last { get; set; }
public override string ToString() => $"{First} {Last}";
}
public class Team
{
public Name First { get; set; }
public Name Second { get; set; }
public Name Third { get; set; }
}
public ObservableCollection<Team> DataList { get; } = new ObservableCollection<Team>();
<DataGrid ItemsSource="{Binding DataList}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="First" Binding="{Binding First}"/>
<DataGridTextColumn Header="Second" Binding="{Binding Second}"/>
<DataGridTextColumn Header="Third" Binding="{Binding Third}"/>
</DataGrid.Columns>
</DataGrid>
Now, I would like to separate the First
and Last
properties of Name
and render them using individual controls, both still inside of the same DataGrid
cell. The motivation here could be to have First
in black and Last
in red, for example, or anything else that requires them to be in two separate controls.
Example of a DateTemplate
(applied as CellTemplate
on DataGridTemplateColumn
):
<DataTemplate x:Key="NameTemplate">
<StackPanel>
<Label Content="{Binding First}"/>
<Label Content="{Binding Last}"/>
</StackPanel>
</DataTemplate>
Example of a DataGridCell
style (applied as CellStyle
on a DataGridTextColumn
):
<Style x:Key="NameStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<DockPanel>
<ContentPresenter Content="{Binding First}"/>
<ContentPresenter Content="{Binding Last}"/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Is there any way to make a code similar to either of the examples above work?
Of course, it is possible to copy-paste the same exact template/style for each column, each accessing the relevant row-level property of Name
, but this is an awful solution that would be a nightmare to maintain and it just does not scale.
I have seen a handful of similar questions, but not a single one of them had a XAML solution that was not extremely complex. This just seems like something that should be pretty easy to do, but I am unable to find a straightforward solution anywhere. The most similar question I found was probably this, but it was never really answered in a way that would help my case.
Two possible solutions were to either build and apply the XAML code (template/style) at runtime or to add an attached property to the column, which would allow me to pass the properties separately. Both of those seem like terrible hacks to me and I would much prefer avoiding them. Another solution is to create a new control (as suggested here) derived from DataGridBoundColumn
(such as this), but I had no luck with that either and it a fairly complicated solution compared to defining a DataTemplate
.
PS: While finishing up the question and looking for the links to other questions and answers, I have stumbled upon the DataGridBoundTemplateColumn
implementation, which pretty much works, but I would still prefer a more XAML-native solution using just a template/style rather than creating a new control that still requires the template on top of itself.