29
  <my:DataGridTemplateColumn 
            CanUserResize="False" 
            Width="150" 
            Header="{Binding MeetingName, Source={StaticResource LocStrings}}" 
            SortMemberPath="MeetingName"> 
  </my:DataGridTemplateColumn>

I have the above column in a Silverlight grid control. But it is giving me a XamlParser error because of how I am trying to set the Header property. Has anyone done this before? I want to do this for multiple languages.

Also my syntax for the binding to a resouce is correct because I tried it in a lable outside of the grid.

Steve Wranovsky
  • 5,503
  • 4
  • 34
  • 52
Kyle
  • 423
  • 1
  • 6
  • 13

8 Answers8

28

You can't Bind to Header because it's not a FrameworkElement. You can make the text dynamic by modifying the Header Template like this:

xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:dataprimitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"

<data:DataGridTemplateColumn>   
   <data:DataGridTemplateColumn.HeaderStyle>
       <Style TargetType="dataprimitives:DataGridColumnHeader">
          <Setter Property="Template">
             <Setter.Value>
                <ControlTemplate>                                        
                  <TextBlock Text="{Binding MeetingName, Source={StaticResource LocStrings}}" />                
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
   </data:DataGridTemplateColumn.HeaderStyle>
</data:DataGridTemplateColumn>
Adam Kinney
  • 1,075
  • 7
  • 5
  • 12
    How do you keep the visual styling from the original header? – Edward Brey Mar 31 '10 at 17:23
  • This helped me out also. I was able to apply it to a DataGridTextColumn in basically the same way. I used the ContentTemplate suggestion also mentioned. – Steve Wranovsky Jul 06 '11 at 22:16
  • 2
    In this question, the solution provided by RobSiklos keeps the visual styling as well. – Y P Oct 29 '12 at 03:43
  • 2
    I think that the accepted answer should be the RobSiklos's one. Not only because keeps the visual styling, but too because provides an easily implementation that keeps the concepts of "style" and "code" separated. – Sandrous Mar 04 '14 at 14:53
14

My workaround was to use an attached property to set the binding automatically:

public static class DataGridColumnHelper
{
    public static readonly DependencyProperty HeaderBindingProperty = DependencyProperty.RegisterAttached(
        "HeaderBinding",
        typeof(object),
        typeof(DataGridColumnHelper),
        new PropertyMetadata(null, DataGridColumnHelper.HeaderBinding_PropertyChanged));

    public static object GetHeaderBinding(DependencyObject source)
    {
        return (object)source.GetValue(DataGridColumnHelper.HeaderBindingProperty);
    }

    public static void SetHeaderBinding(DependencyObject target, object value)
    {
        target.SetValue(DataGridColumnHelper.HeaderBindingProperty, value);
    }

    private static void HeaderBinding_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumn column = d as DataGridColumn;

        if (column == null) { return; }

        column.Header = e.NewValue;
    }
}

Then, in the XAML:

<data:DataGridTextColumn util:DataGridColumnHelper.HeaderBinding="{Binding MeetingName, Source={StaticResource LocStrings}}" />
RobSiklos
  • 8,348
  • 5
  • 47
  • 77
  • 1
    Unlike the accepted solution, this solution can do what the accepted solution does plus preserving the original styling. It is working for me. +1 – Y P Oct 29 '12 at 03:41
  • This works for me too. This is absolutely PERFECT, and what I'm looking for: style and code behind, separated as concepts, separated in XAML. Thank you very much, this should be the accepted solution. – Sandrous Mar 04 '14 at 14:47
  • Like this solution better as it preserves responsibility and separation of concerns. – Pingi Jan 03 '16 at 12:08
11

To keep the visual styling from the original header, use ContentTemplate instead of Template:

<Setter Property="ContentTemplate">
<Setter.Value>
    <DataTemplate>
        <Image Source="<image url goes here>"/>
    </DataTemplate>
</Setter.Value>

Lars Holm Jensen
  • 1,645
  • 1
  • 12
  • 14
  • Can't seem to get binding to work in the DataTemplate on a TextBlock: replaces the Image in your example. – ptoinson Oct 20 '10 at 18:30
  • This doesn't help with keeping the visual styling in my experience. – Steven Oxley Nov 10 '10 at 21:20
  • 5
    This method is definitely the way to go, and it DOES keep the visual styling. If you re-template the whole header, it becomes a white box containing whatever you put there. However, if you use a ContentTemplate, the existing background of the default header style is maintained. – RobSiklos Apr 28 '11 at 20:56
  • 1
    Also, if you specify the Source of your Binding as some StaticResource (like in the original post), it works. – RobSiklos Apr 28 '11 at 20:57
2

Found an interesting workaround that also works with the wpflocalizeaddin.codeplex.com:

Created by Slyi

It uses an IValueConverter:

public class BindingConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value.GetType().Name == "Binding")
        {
            ContentControl cc = new ContentControl();
            cc.SetBinding(ContentControl.ContentProperty, value as Binding);
            return cc;
        }
        else return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {

        return null;
    }
}

And a style for the DataGridColumnHeader

<UserControl.Resources>
    <local:BindingConverter x:Key="BindCon"/>
    <Style x:Key="ColBinding" TargetType="dataprimitives:DataGridColumnHeader" >
        <Setter Property="ContentTemplate" >
            <Setter.Value>
                <DataTemplate>
                    <ContentPresenter Content="{Binding Converter={StaticResource BindCon}}"  />
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>

so that you can keep your favorite binding syntax on the Header attribute

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <TextBox Text="binding header" x:Name="tbox" />

        <data:DataGrid ItemsSource="{Binding AllPeople,Source={StaticResource folks}}" AutoGenerateColumns="False" ColumnHeaderStyle="{StaticResource ColBinding}"  >
            <data:DataGrid.Columns>
                <data:DataGridTextColumn Binding="{Binding ID}" 

                                         Header="{Binding Text, ElementName=tbox}" />
                <data:DataGridTextColumn Binding="{Binding Name}" 

                                         Header="hello" />
            </data:DataGrid.Columns>
        </data:DataGrid>
    </StackPanel>

</Grid>

http://cid-289eaf995528b9fd.skydrive.live.com/self.aspx/Public/HeaderBinding.zip

Rudi
  • 3,124
  • 26
  • 35
2

It does seem much simpler to set the value in code, as mentioned above:

dg1.Columns[3].Header = SomeDynamicValue;

Avoids using the Setter Property syntax, which in my case seemed to mess up the styling, even though I did try using ContentTemplate as well as Template.

One point I slipped up on was that it is better to use the dg1.Columns[3].Header notation rather than trying to reference a named column.

I had named one of my columns and tried to reference that in code but got null exceptions. Using the Columns[index] method worked well, and I could assign the Header a text string based on localization resources.

Anne
  • 26,765
  • 9
  • 65
  • 71
Steve
  • 69
  • 3
1

Please note in solution provided by RobSiklos, Source {staticResource...} is the Key, if you plan to pass the RelativeSource like

Binding DataContext.SelectedHistoryTypeItem,RelativeSource={RelativeSource AncestorType=sdk:DataGrid},

it may not work

kleopatra
  • 51,061
  • 28
  • 99
  • 211
Jit
  • 11
  • 1
1

Why not simply set this in code:

dg1.Columns[3].Header = SomeDynamicValue;
Jersey Dude
  • 527
  • 1
  • 5
  • 16
0

I got some solution for the binding. Since you use DataGridTemlateColumn, subclass it and add a property of type Binding named for instance "HeaderBinding". Now you can bind to that property from the XAML. Next, you should propagate the binding to the TextBlock in the DataTemplate of your header. For instance, you can do it with OnLoaded event of that TextBlock.

 HeaderTextBlock.SetBinding(TextBlock.TextProperty, HeaderBinding);

That's it. If you have more columns and want to use only one DataTemplate then it's a bit more complicated, but the idea is the same.