views:

1200

answers:

2

I am trying to move custom DataGrid column definition into a UserControl.

MyComboBoxColumn.xaml

<dg:DataGridTemplateColumn 
    x:Class="WpfDecomposition.MyComboBoxColumn"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WpfToolkit"
    x:Name="_this"
    >

    <dg:DataGridTemplateColumn.Header>
        <Button Content="{Binding MyHeader, ElementName=_this}" ></Button>
    </dg:DataGridTemplateColumn.Header>

</dg:DataGridTemplateColumn>

MyComboBoxColumn.cs

public partial class MyComboBoxColumn : DataGridTemplateColumn
{
    public MyComboBoxColumn()
    {
        InitializeComponent();
    }

    public static DependencyProperty MyHeaderProperty = 
        DependencyProperty.Register("MyHeader", typeof(string), typeof(MyComboBoxColumn), new PropertyMetadata("TEST"));
}

Main windows XAML:

<dg:DataGrid CanUserAddRows="True" AutoGenerateColumns="False">
    <dg:DataGrid.Columns>
        <my:MyComboBoxColumn />
    </dg:DataGrid.Columns>
</dg:DataGrid>

I would expect to see a button "TEST" in the column's header, but instead I see the empty button. Looks like the binding is broken. What is wrong?

+2  A: 

It's not working because it can't find an element with the name _this. I get the following error in the Output window when I debug your code in Visual Studio:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=_this'. BindingExpression:Path=MyHeader; DataItem=null; target element is 'Button' (Name='TestButton'); target property is 'Content' (type 'Object')

As for why it can't find it - I think that is because WPF bindings use the visual tree to find the source of the binding. In this case, the MyComboBoxColumn is not in the visual tree, so therefore it can't find an element with that name.

I also tried using RelativeSource to find the element, but that didn't work either - likely for the same reason.

The only thing that I could get to work is to set the DataContext of the button to the column itself in the constructor:

public MyComboBoxColumn()
{
    InitializeComponent();

    this.TestButton.DataContext = this;
}

And then change the binding in the XAML:

<tk:DataGridTemplateColumn.Header>
    <Button Content="{Binding Path=MyHeader}" x:Name="TestButton" />
</tk:DataGridTemplateColumn.Header>

That doesn't seem like the best way to do it, but at least it works.

Andy
Yes, your solution works, thank you!
alex2k8
A: 

If you don't want to or can't set the DataContext in the constructor (e.g. when creting columns dynamically in the code), set the column's Header property to the object you want to bind to (the data context) and then you can bind to this object in the HeaderStyle data template.

See this question for details.

Lukas Cenovsky