'Why does RelativeSource binding randomly fail to find source?

I need someone to explain to me why two bindings randomly fail to find the binding Source. They switch between failing and working even when I precisely repeat the exact same 2 steps over and over: (Start app, Navigate to page. Start app, navigate to page, etc). I might get 5 failures in row, then it works, then fails 7 more times, then works twice, etc.

Here is what "failure" looks like in the VS output window

System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='MyCompany.Controls.LayerView, AncestorLevel='1''. BindingExpression:Path=ScanHeight; DataItem=null; target element is 'Path' (Name=''); target property is 'Data' (type 'Geometry')
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='MyCompany.Controls.LayerView', AncestorLevel='1''. BindingExpression:Path=ScanWidth; DataItem=null; target element is 'Path' (Name=''); target property is 'Data' (type 'Geometry')

Here are the bindings. I want to use properties of the control containing the binding, so I use RelativeSource FindAncestor

<ctrl:LayerView x:Name="MainLayerView">
    <ctrl:LayerView.Layers> 
        <Path>
            <Path.Data>
                <MultiBinding Converter="{StaticResource CvtGuideOption}">
                    <!-- These fail to find the LayerView, but only *sometimes* -->
                    <Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:LayerView}}" Path="ScanHeight"/>
                    <Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:LayerView}}" Path="ScanWidth"/>
                </MultiBinding>
            </Path.Data>
        </Path>
    </ctrl:LayerView.Layers>
</ctrl:LayerView>

I could live with these always working or never working, but randomly? (I tried using ElementName instead of RelativeAncestor binding but the effect was the same. Random.)

Finally, I stumbled upon an approach (below) that seems to get around this. But I don't know if I can trust it.

<ctrl:LayerView x:Name="MainLayerView">
    <ctrl:LayerView.Layers>
         <Path Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type ctrl:LayerView}}}">
            <Path.Data>
                <MultiBinding Converter="{StaticResource CvtGuideOption}">
                    <Binding RelativeSource="{RelativeSource Self}" Path="Tag.(ctrl:LayerView.ScanWidth)"/>
                    <Binding RelativeSource="{RelativeSource Self}" Path="Tag.(ctrl:LayerView.ScanWidth)"/>
                </MultiBinding>
            </Path.Data>
        </Path>
    </ctrl:LayerView.Layers>
</ctrl:LayerView>

As you can see, my hack is to first bind the LayerView source into a the Tag of the Path and then use that as the source of the individual bindings. So far, this appears to always find the source.

But to be sure I've fixed the problem I need to understand what it was.

About the only WAG I can make is that this is somehow related to the nature of the Layers collection I'm filling out. It's not a DependencyProperty. I'm trying to mimic what ItemsControl.Items does. Could that be a factor?

Additional Info: The LayerView control looks like this. It's large so I've shown only the relevant bits here.

// Custom control with a collection of layers and two Dependency properties.

public class LayerView : MultiSelector
{         
    // Layers property is not a DependencyProperty.  Just a public
    // Collection of objects.  This is me trying to mimic how ItemsControl 
    // implements its "Items" property  
    private Collection<object>? _layers;
    [Bindable(true), Category("Content")]
    public Collection<object> Layers => _layers ??= new Collection<object>();


    public static readonly DependencyProperty ScanWidthProperty =
        DependencyProperty.Register(
            nameof(ScanWidth),
            typeof(double),
            typeof(LayerView),
            new FrameworkPropertyMetadata(
                2464.0, // Default width from camera
                FrameworkPropertyMetadataOptions.AffectsRender));

    public double ScanWidth
    {
        get => (double)GetValue(ScanWidthProperty);
        set => SetValue(ScanWidthProperty, value);
    }


    public static readonly DependencyProperty ScanHeightProperty =
        DependencyProperty.Register(
            nameof(ScanHeight),
            typeof(double),
            typeof(LayerView),
            new FrameworkPropertyMetadata(
                2056.0, // Default image height from camera
                FrameworkPropertyMetadataOptions.AffectsRender));

    public double ScanHeight
    {
        get => (double)GetValue(ScanHeightProperty);
        set => SetValue(ScanHeightProperty, value);
    }
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source