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.

8 Answers 8

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>
Sign up to request clarification or add additional context in comments.

4 Comments

How do you keep the visual styling from the original header?
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.
In this question, the solution provided by RobSiklos keeps the visual styling as well.
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.
13

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}}" />

3 Comments

Unlike the accepted solution, this solution can do what the accepted solution does plus preserving the original styling. It is working for me. +1
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.
Like this solution better as it preserves responsibility and separation of concerns.
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>

4 Comments

Can't seem to get binding to work in the DataTemplate on a TextBlock: <TextBlock Text="{Binding HeaderText}"/> replaces the Image in your example.
This doesn't help with keeping the visual styling in my experience.
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.
Also, if you specify the Source of your Binding as some StaticResource (like in the original post), it works.
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

Comments

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.

Comments

1

Why not simply set this in code:

dg1.Columns[3].Header = SomeDynamicValue;

Comments

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

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.