Associating a DataGrid Column DataTemplate with an Attached Property

I am trying to configure a DataGridColumnHeader to display multiple text fields instead of showing only the header text provided by the DataGridColumn.Header property.
If I didn't miss anything, I just need to create a DataTemplate and bind to the properties of the parent object. This works fine for the DataGridColumn.Header property, but
attachment to the attached property failed.

To complete the implementation of the implemented property:

public static class CustomHeader { public static string GetUnit(DependencyObject obj) { return (string)obj.GetValue(UnitProperty); } public static void SetUnit(DependencyObject obj, string value) { obj.SetValue(UnitProperty, value); } public static readonly DependencyProperty UnitProperty = DependencyProperty.RegisterAttached( "Unit", typeof(string), typeof(CustomHeader), new FrameworkPropertyMetadata(null)); } 

Usage in Xaml-Markup:

 <DataGrid x:Name="tObjectDataGrid" Margin="10,50,10,10" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding ObjectList}" RowDetailsVisibilityMode="VisibleWhenSelected" > <DataGrid.Resources> <DataTemplate x:Key="CustomHeaderTemplate"> <StackPanel> <TextBlock Text="{Binding}" /> <TextBlock Text="{Binding Path=(cust:CustomHeader.Unit)}" /> <-- attached binding doesn't work :( </StackPanel> </DataTemplate> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn x:Name="SpeedColumn" Width="1*" Binding="{Binding Speed}" Header="Speed" HeaderTemplate="{StaticResource CustomHeaderTemplate}" cust:CustomHeader.Unit="[m/s]" /> </DataGrid.Columns> </DataGrid> 

I really appreciate any comment or website link that clarifies what I don't see here. Thanks in advance.

+4
source share
2 answers

You should use a multi-value converter ( msdn ).

XAML:

 <DataGrid x:Name="tObjectDataGrid" Margin="10,50,10,10" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding ObjectList}" RowDetailsVisibilityMode="VisibleWhenSelected" > <DataGrid.Resources> <cust:UnitConverter x:Key="unitCon" /> <DataTemplate x:Key="CustomHeaderTemplate"> <StackPanel> <TextBlock Text="{Binding}" /> <TextBlock> <TextBlock.Text> <MultiBinding Converter="{StaticResource unitCon}"> <Binding Path="Columns" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGrid}" /> <Binding Path="." /> </MultiBinding> </TextBlock.Text> </TextBlock> </StackPanel> </DataTemplate> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn x:Name="SpeedColumn" Width="1*" Binding="{Binding Speed}" Header="Speed" HeaderTemplate="{StaticResource CustomHeaderTemplate}" cust:CustomHeader.Unit="[m/s]" /> <DataGridTextColumn x:Name="SpeedColumn2" Width="1*" Binding="{Binding Speed2}" Header="Speed2" HeaderTemplate="{StaticResource CustomHeaderTemplate}" cust:CustomHeader.Unit="[km/h]" /> </DataGrid.Columns> </DataGrid> 

Code for:

 public class UnitConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { string result = null; ObservableCollection<DataGridColumn> columns = values[0] as ObservableCollection<DataGridColumn>; string headerName = values[1].ToString(); var coll = columns.Where(x => x.Header.ToString() == headerName).FirstOrDefault(); if (coll != null) result = CustomHeader.GetUnit(coll); return result; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } 

I think the best solution would be to create a new class, for example HeaderData , and after that you can instantiate it in xaml and bind to this class.

Example:

Class for storing header data:

 class HeaderData { public string Name { get; set; } public string Unit { get; set; } } 

XAML Code:

 <DataGrid x:Name="tObjectDataGrid" Margin="10,50,10,10" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding ObjectList}" RowDetailsVisibilityMode="VisibleWhenSelected" > <DataGrid.Resources> <DataTemplate x:Key="CustomHeaderTemplate"> <StackPanel> <TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Unit}" /> </StackPanel> </DataTemplate> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn x:Name="SpeedColumn" Width="1*" Binding="{Binding Speed}" HeaderTemplate="{StaticResource CustomHeaderTemplate}"> <DataGridTextColumn.Header> <cust:HeaderData Name="Speed" Unit="[m/s]" /> </DataGridTextColumn.Header> </DataGridTextColumn> <DataGridTextColumn x:Name="SpeedColumn2" Width="1*" Binding="{Binding Speed2}" HeaderTemplate="{StaticResource CustomHeaderTemplate}"> <DataGridTextColumn.Header> <cust:HeaderData Name="Speed2" Unit="[km/h]" /> </DataGridTextColumn.Header> </DataGridTextColumn> </DataGrid.Columns> </DataGrid> 
+3
source

At first glance, your code is normal, it should work. But when your dependency property is set to DataGridTextColumn , SetUnit not called and the value of the Unit variable is NULL . I tried to assign the value of the related dependency property in Window (since it is attached, you can set its value anywhere) in this case should work:

 <Window x:Class="DataGridAttachedHelp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DataGridAttachedHelp" local:CustomHeader.Unit="[m/s]" Name="MyWindow" Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen"> <Grid> <DataGrid x:Name="tObjectDataGrid" Margin="10,50,10,10" AutoGenerateColumns="False" EnableRowVirtualization="True" RowDetailsVisibilityMode="VisibleWhenSelected" > <DataGrid.Resources> <DataTemplate x:Key="CustomHeaderTemplate"> <StackPanel> <TextBlock Text="{Binding}" /> <TextBlock Text="{Binding Path=(local:CustomHeader.Unit), ElementName=MyWindow}" /> </StackPanel> </DataTemplate> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn x:Name="SpeedColumn" Width="1*" Binding="{Binding Speed}" Header="Speed" HeaderTemplate="{StaticResource CustomHeaderTemplate}" /> </DataGrid.Columns> </DataGrid> </Grid> </Window> 

In your case, the property does not work, because you must specify the source of the properties, for example: ElementName , Source . So just add the name DataGridTextColumn to the ElementName parameter:

 <TextBlock Text="{Binding Path=(local:CustomHeader.Unit), ElementName=SpeedColumn}" /> 
+1
source

Source: https://habr.com/ru/post/1493811/


All Articles