Dominic Jonas
Dominic Jonas

Reputation: 5025

Cannot find source for binding with reference in nested Style / DataGrid DataTemplates

I have a Control (ZlsUnitBrowserActive) with two DependencyProperties MaxWidthIdentifier/PropertiesDataTemplate. I'm stuck when try to bind these two properties in the Style:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='PlcFramework.Zls.Production.ProductionControls.ZlsUnitBrowserActive', AncestorLevel='1''. BindingExpression:Path=PropertiesDataTemplate; DataItem=null; target element is 'DataGridTemplateColumn' (HashCode=30225241); target property is 'CellTemplate' (type 'DataTemplate')

<Style TargetType="productionControls:ZlsUnitBrowserActive">
    <Style.Setters>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="productionControls:ZlsUnitBrowserActive">
                    <DataGrid>
                        <DataGrid.Columns>
                            <DataGridTemplateColumn>
                                <DataGridTemplateColumn.CellTemplate 
                                    MaxWidth="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=productionControls:ZlsUnitBrowserActive}, Path=MaxWidthIdentifier}"
                                    CellTemplate="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=productionControls:ZlsUnitBrowserActive}, Path=PropertiesDataTemplate}"/>
                            </DataGridTemplateColumn>
                        </DataGrid.Columns>
                    </DataGrid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>

Is there a special trick? Also tried by caching the ZlsUnitBrowserActive instance in DataGrid.Tag (see post: https://stackoverflow.com/a/3668699/6229375), without success.

Upvotes: 0

Views: 2591

Answers (2)

mm8
mm8

Reputation: 169400

The reason why you can't bind a property of a DataGridColumn to a visual ancestor using a RelativeSource is that the column itself is not part of the visual tree so it has no ancestors to bind to.

The DataGrid control is part of the visual tree though and the "BindingProxy" can be used to bind to and "capture" a DataContext, for example using a RelativeSource of the DataGrid.

Thomas Levesque has written a blog post that explains this in detail: https://thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/.

Upvotes: 2

Dominic Jonas
Dominic Jonas

Reputation: 5025

I found the solution. The trick is to use a "BindingProxy".

I didn't really understand exactly why I had to solve it this way. Maybe someone can give me an explanation or post a better solution. 😊

public class BindingProxy : Freezable
{
    public static readonly DependencyProperty DataProperty;

    static BindingProxy()
    {
        DataProperty = DependencyProperty.Register(nameof(Data), typeof(object), typeof(BindingProxy));
    }

    public object Data
    {
        get => GetValue(DataProperty);
        set => SetValue(DataProperty, value);
    }

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }
}
<Style TargetType="productionControls:ZlsUnitBrowserActive">
    <Style.Setters>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="productionControls:ZlsUnitBrowserActive">
                    <DataGrid>
                        <DataGrid.Resources>
                            <xamlHelper:BindingProxy x:Key="DataContextBindingProxy" Data="{Binding RelativeSource={RelativeSource TemplatedParent}}"/> 
                        </DataGrid.Resources>
                        <DataGrid.Columns>
                            <DataGridTemplateColumn>
                                <DataGridTemplateColumn.CellTemplate 
                                    MaxWidth="{Binding Path=Data.MaxWidthIdentifier, Source={StaticResource DataContextBindingProxy}}"
                                    CellTemplate="{Binding Path=Data.MaxWidthIdentifier, Source={StaticResource PropertiesDataTemplate}"/>
                            </DataGridTemplateColumn>
                        </DataGrid.Columns>
                    </DataGrid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>

Upvotes: 0

Related Questions