I have multiple DataGrid
objects in different tabs of WPF window that share the same columns.
I can define a column as a resource like this:
<TabControl.Resources>
<DataGridTextColumn x:Key="NameGridColumn" Binding="{Binding Name}" IsReadOnly="True" Width="*">
<DataGridTextColumn.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name" VerticalAlignment="Center"/>
<Button Command="{Binding SortByNameCommand}" Content="▲" Background="Transparent" BorderThickness="0" Margin="5 0 0 0"/>
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</TabControl.Resources>
But how can I then use these resource columns in a DataGrid
?
<TabItem Header="Tab 1">
<DataGrid ItemsSource="{Binding ItemsCollection}" CanUserAddRows="False" CanUserDeleteRows="False" CanUserSortColumns="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<????>
<!-- notice that I have many columns, not just one -->
</DataGrid.Columns>
</DataGrid>
</TabItem>
Not the ideal answer, but fairly good.
This is an answer specific for data grid column headers (the columns you define are not actual controls, but representations for controls that will be generated later). If you want an answer for actual controls in general, you can resort to this answer
You can use a DataTemplate
to define the headers:
<TabControl.Resources>
<!--Converter to change arrow buttons appearance-->
<local:EnabledToBrushConverter x:Key="EnabledToBrushConverter"/>
<!--Style for all arrow buttons-->
<Style TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Margin" Value="5 0 0 0"/>
</Style>
<!-- each data template is a header -->
<DataTemplate x:Key="NameGridHeader">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name" VerticalAlignment="Center"/>
<Button Command="{Binding Path=DataContext.SortByNameCommand, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}" Content="▲"
Foreground="{Binding Path=DataContext.IsSortByName, Converter={StaticResource EnabledToBrushConverter}, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="AnotherHeader">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Another value" VerticalAlignment="Center"/>
<Button Command="{Binding Path=DataContext.SortByAnotherValueCommand, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}" Content="▼"
Foreground="{Binding Path=DataContext.IsSortByAnotherValue, Converter={StaticResource EnabledToBrushConverter}, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}"/>
</StackPanel>
</DataTemplate>
</TabControl.Resources>
And you can use the HeaderTemplate
property of a DataGridTextColumn
or a DataGridTemplateColumn
.
<DataGrid.Columns>
<DataGridTextColumn Header="Type" Binding="{Binding ItemType}" IsReadOnly="True"/>
<DataGridTextColumn HeaderTemplate="{StaticResource NameGridHeader}" Binding="{Binding Name}" IsReadOnly="True" Width="*"/>
<DataGridTextColumn HeaderTemplate="{StaticResource AnotherHeader}" Binding="{Binding AnotherValueString}" IsReadOnly="True"/>
</DataGrid.Columns>
<!-- Define as many datagrids as you like using the same column header templates -->
The downsides are:
DataGridTemplateColumn
's CellTemplate
or CellEditingTemplate
) or styles if it's the caseRelativeSource
set to some element above it