Привязка столбца DataGrid DataTemplate к присоединенному свойству

Я пытаюсь настроить DataGridColumnHeader для отображения нескольких текстовых полей вместо
показывает только текст заголовка, предоставленный свойством DataGridColumn.Header.
Если я что-то не пропустил, мне просто нужно создать DataTemplate и привязать к свойствам
родительского объекта. Это прекрасно работает для свойства DataGridColumn.Header, но
привязка к присоединенному имуществу не выполняется.

Для полноты реализации прикрепленного свойства:

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));
}

Использование в Xaml-разметке:

<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>  

Я действительно ценю любой комментарий или веб-ссылку, которая разъясняет то, что я пропускаю здесь Заранее спасибо.

2 ответа

Решение

Вы должны использовать многозначный преобразователь ( 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>

Код-за:

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();
    }
}

Я думаю, что лучшим решением будет создание нового класса, например HeaderData и после этого вы можете создать его экземпляр в xaml и привязать его к этому классу.

Пример:

Класс для хранения данных заголовка:

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

Код XAML:

<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>

На первый взгляд ваш код нормальный, должен работать. Но когда ваше свойство зависимости установлено DataGridTextColumn, SetUnit не вызывается и переменная Unit значение NULL, Я пытался присвоить значение присоединенного свойства зависимости в Window (поскольку он прикреплен, вы можете установить его значение где угодно), в этом случае должно работать:

<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>

В вашем случае свойство не сработало, поскольку необходимо указать источник свойств, например: ElementName, Source, Поэтому просто добавьте название DataGridTextColumn в параметре ElementName:

<TextBlock Text="{Binding Path=(local:CustomHeader.Unit), ElementName=SpeedColumn}" />
Другие вопросы по тегам