3

While constructing an object in xaml, the DataContext seems to resolve properly most of the time, but constructing that same object directly in the scope of an Attached Property seems to block DataContext inheritance.

In this, there are several definitions required for recreation. I'm happy to show the code, but for brevity's sake here is the outline:

  1. A view model, ViewModel, with a property, ViewModel.MyProperty of type string set to "123456789abc"
  2. A custom object, class FrameworkObject : FrameworkElement - no UI defined, but has a DataContext. This object has a defined dependency property, string FrameworkObject.MyDependencyProperty
  3. An attached property, AttachedProperty.FrameworkObject, which takes an object of type FrameworkObject
  4. A .Net Framework WPF Application where we will do the testing

Creating the object as an element in the Visual Tree successfully binds the value

    <!--
        System.Windows.Data Warning: 56 : Created BindingExpression (hash=52203868) for Binding (hash=27504314)
        System.Windows.Data Warning: 58 :   Path: 'MyProperty'
        System.Windows.Data Warning: 60 : BindingExpression (hash=52203868): Default mode resolved to OneWay
        System.Windows.Data Warning: 61 : BindingExpression (hash=52203868): Default update trigger resolved to PropertyChanged
        System.Windows.Data Warning: 62 : BindingExpression (hash=52203868): Attach to StackOverflowExamples.FrameworkObject.MyProperty (hash=34181910)
        System.Windows.Data Warning: 67 : BindingExpression (hash=52203868): Resolving source
        System.Windows.Data Warning: 70 : BindingExpression (hash=52203868): Found data context element: FrameworkObject (hash=34181910) (OK)
        System.Windows.Data Warning: 78 : BindingExpression (hash=52203868): Activate with root item ViewModel (hash=66824994)
        System.Windows.Data Warning: 108 : BindingExpression (hash=52203868):   At level 0 - for ViewModel.MyProperty found accessor RuntimePropertyInfo(MyProperty)
        System.Windows.Data Warning: 104 : BindingExpression (hash=52203868): Replace item at level 0 with ViewModel (hash=66824994), using accessor RuntimePropertyInfo(MyProperty)
        System.Windows.Data Warning: 101 : BindingExpression (hash=52203868): GetValue at level 0 from ViewModel (hash=66824994) using RuntimePropertyInfo(MyProperty): '123456789abc'
        System.Windows.Data Warning: 80 : BindingExpression (hash=52203868): TransferValue - got raw value '123456789abc'
        System.Windows.Data Warning: 89 : BindingExpression (hash=52203868): TransferValue - using final value '123456789abc'
    -->
    <local:FrameworkObject x:Name="CreatedInPanel" MyDependencyProperty="{Binding MyProperty, diag:PresentationTraceSources.TraceLevel=High}" />
    <TextBlock local:AttachedProperty.FrameworkObject="{Binding Path='', ElementName=CreatedInPanel}" Style="{StaticResource DisplayFromAttached}" />

Creating the object within the scope of the TextBlock fails to bind properly

        <!--
        System.Windows.Data Warning: 56 : Created BindingExpression (hash=53517805) for Binding (hash=3663598)
        System.Windows.Data Warning: 58 :   Path: 'MyProperty'
        System.Windows.Data Warning: 60 : BindingExpression (hash=53517805): Default mode resolved to OneWay
        System.Windows.Data Warning: 61 : BindingExpression (hash=53517805): Default update trigger resolved to PropertyChanged
        System.Windows.Data Warning: 62 : BindingExpression (hash=53517805): Attach to StackOverflowExamples.FrameworkObject.MyProperty (hash=51442863)
        System.Windows.Data Warning: 67 : BindingExpression (hash=53517805): Resolving source
        System.Windows.Data Warning: 70 : BindingExpression (hash=53517805): Found data context element: FrameworkObject (hash=51442863) (OK)
        System.Windows.Data Warning: 71 : BindingExpression (hash=53517805): DataContext is null
        System.Windows.Data Warning: 65 : BindingExpression (hash=53517805): Resolve source deferred
        System.Windows.Data Warning: 67 : BindingExpression (hash=53517805): Resolving source
        System.Windows.Data Warning: 70 : BindingExpression (hash=53517805): Found data context element: FrameworkObject (hash=51442863) (OK)
        System.Windows.Data Warning: 71 : BindingExpression (hash=53517805): DataContext is null
        System.Windows.Data Warning: 67 : BindingExpression (hash=53517805): Resolving source
        System.Windows.Data Warning: 70 : BindingExpression (hash=53517805): Found data context element: FrameworkObject (hash=51442863) (OK)
        System.Windows.Data Warning: 71 : BindingExpression (hash=53517805): DataContext is null
        System.Windows.Data Warning: 67 : BindingExpression (hash=53517805): Resolving source
        System.Windows.Data Warning: 70 : BindingExpression (hash=53517805): Found data context element: FrameworkObject (hash=51442863) (OK)
        System.Windows.Data Warning: 71 : BindingExpression (hash=53517805): DataContext is null
        System.Windows.Data Warning: 67 : BindingExpression (hash=53517805): Resolving source  (last chance)
        System.Windows.Data Warning: 70 : BindingExpression (hash=53517805): Found data context element: FrameworkObject (hash=51442863) (OK)
        System.Windows.Data Warning: 78 : BindingExpression (hash=53517805): Activate with root item <null>
        System.Windows.Data Warning: 106 : BindingExpression (hash=53517805):   Item at level 0 is null - no accessor
        System.Windows.Data Warning: 80 : BindingExpression (hash=53517805): TransferValue - got raw value {DependencyProperty.UnsetValue}
        System.Windows.Data Warning: 88 : BindingExpression (hash=53517805): TransferValue - using fallback/default value ''
        System.Windows.Data Warning: 89 : BindingExpression (hash=53517805): TransferValue - using final value ''
    -->
    <TextBlock Style="{StaticResource DisplayFromAttached}">
        <local:AttachedProperty.FrameworkObject>
            <local:FrameworkObject x:Name="CreatedInScope" MyDependencyProperty="{Binding MyProperty, diag:PresentationTraceSources.TraceLevel=High}" />
        </local:AttachedProperty.FrameworkObject>
    </TextBlock>

Using a data proxy will resolve the binding even when created in a control's scope

        <!--
        System.Windows.Data Warning: 56 : Created BindingExpression (hash=6968762) for Binding (hash=14964341)
        System.Windows.Data Warning: 58 :   Path: 'DataContext.MyProperty'
        System.Windows.Data Warning: 60 : BindingExpression (hash=6968762): Default mode resolved to OneWay
        System.Windows.Data Warning: 61 : BindingExpression (hash=6968762): Default update trigger resolved to PropertyChanged
        System.Windows.Data Warning: 62 : BindingExpression (hash=6968762): Attach to StackOverflowExamples.FrameworkObject.MyProperty (hash=47145209)
        System.Windows.Data Warning: 67 : BindingExpression (hash=6968762): Resolving source
        System.Windows.Data Warning: 70 : BindingExpression (hash=6968762): Found data context element: <null> (OK)
        System.Windows.Data Warning: 78 : BindingExpression (hash=6968762): Activate with root item FrameworkElement (hash=339559)
        System.Windows.Data Warning: 108 : BindingExpression (hash=6968762):   At level 0 - for FrameworkElement.DataContext found accessor DependencyProperty(DataContext)
        System.Windows.Data Warning: 104 : BindingExpression (hash=6968762): Replace item at level 0 with FrameworkElement (hash=339559), using accessor DependencyProperty(DataContext)
        System.Windows.Data Warning: 101 : BindingExpression (hash=6968762): GetValue at level 0 from FrameworkElement (hash=339559) using DependencyProperty(DataContext): <null>
        System.Windows.Data Warning: 106 : BindingExpression (hash=6968762):   Item at level 1 is null - no accessor
        System.Windows.Data Warning: 80 : BindingExpression (hash=6968762): TransferValue - got raw value {DependencyProperty.UnsetValue}
        System.Windows.Data Warning: 88 : BindingExpression (hash=6968762): TransferValue - using fallback/default value ''
        System.Windows.Data Warning: 89 : BindingExpression (hash=6968762): TransferValue - using final value ''
        System.Windows.Data Warning: 96 : BindingExpression (hash=6968762): Got PropertyChanged event from FrameworkElement (hash=339559) for DataContext
        System.Windows.Data Warning: 101 : BindingExpression (hash=6968762): GetValue at level 0 from FrameworkElement (hash=339559) using DependencyProperty(DataContext): ViewModel (hash=66824994)
        System.Windows.Data Warning: 108 : BindingExpression (hash=6968762):   At level 1 - for ViewModel.MyProperty found accessor RuntimePropertyInfo(MyProperty)
        System.Windows.Data Warning: 104 : BindingExpression (hash=6968762): Replace item at level 1 with ViewModel (hash=66824994), using accessor RuntimePropertyInfo(MyProperty)
        System.Windows.Data Warning: 101 : BindingExpression (hash=6968762): GetValue at level 1 from ViewModel (hash=66824994) using RuntimePropertyInfo(MyProperty): '123456789abc'
        System.Windows.Data Warning: 80 : BindingExpression (hash=6968762): TransferValue - got raw value '123456789abc'
        System.Windows.Data Warning: 89 : BindingExpression (hash=6968762): TransferValue - using final value '123456789abc'
    -->
    <ContentControl Content="{StaticResource DataProxy}" Visibility="Collapsed" />
    <TextBlock Style="{StaticResource DisplayFromAttached}">
        <local:AttachedProperty.FrameworkObject>
            <local:FrameworkObject x:Name="DataProxyBinding" MyDependencyProperty="{Binding DataContext.MyProperty, Source={StaticResource DataProxy}, diag:PresentationTraceSources.TraceLevel=High}" />
        </local:AttachedProperty.FrameworkObject>
    </TextBlock>

And the most confusing to me. Constructing the FrameworkObject within a ContentControl.Content seems to bind perfectly fine

        <!--
        System.Windows.Data Warning: 56 : Created BindingExpression (hash=63642613) for Binding (hash=38750844)
        System.Windows.Data Warning: 58 :   Path: 'MyProperty'
        System.Windows.Data Warning: 60 : BindingExpression (hash=63642613): Default mode resolved to OneWay
        System.Windows.Data Warning: 61 : BindingExpression (hash=63642613): Default update trigger resolved to PropertyChanged
        System.Windows.Data Warning: 62 : BindingExpression (hash=63642613): Attach to StackOverflowExamples.FrameworkObject.MyProperty (hash=16347077)
        System.Windows.Data Warning: 67 : BindingExpression (hash=63642613): Resolving source
        System.Windows.Data Warning: 70 : BindingExpression (hash=63642613): Found data context element: FrameworkObject (hash=16347077) (OK)
        System.Windows.Data Warning: 78 : BindingExpression (hash=63642613): Activate with root item ViewModel (hash=66824994)
        System.Windows.Data Warning: 108 : BindingExpression (hash=63642613):   At level 0 - for ViewModel.MyProperty found accessor RuntimePropertyInfo(MyProperty)
        System.Windows.Data Warning: 104 : BindingExpression (hash=63642613): Replace item at level 0 with ViewModel (hash=66824994), using accessor RuntimePropertyInfo(MyProperty)
        System.Windows.Data Warning: 101 : BindingExpression (hash=63642613): GetValue at level 0 from ViewModel (hash=66824994) using RuntimePropertyInfo(MyProperty): '123456789abc'
        System.Windows.Data Warning: 80 : BindingExpression (hash=63642613): TransferValue - got raw value '123456789abc'
        System.Windows.Data Warning: 89 : BindingExpression (hash=63642613): TransferValue - using final value '123456789abc'
    -->
    <ContentControl x:Name="ImplicitContent">
        <ContentControl.Template>
            <ControlTemplate TargetType="ContentControl">
                <TextBlock local:AttachedProperty.FrameworkObject="{TemplateBinding Content}" Style="{StaticResource DisplayFromAttached}" />
            </ControlTemplate>
        </ContentControl.Template>

        <local:FrameworkObject MyDependencyProperty="{Binding MyProperty, diag:PresentationTraceSources.TraceLevel=High}" />
    </ContentControl>

Through this, I've used a style, DisplayFromAttached, which is found in the enclosing panel's resource dictionary and is defined thus:

<Style x:Key="DisplayFromAttached" TargetType="TextBlock">
    <Setter Property="Text" Value="{Binding Path=(local:AttachedProperty.FrameworkObject).MyDependencyProperty, RelativeSource={RelativeSource Self}}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=(local:AttachedProperty.FrameworkObject).MyDependencyProperty, RelativeSource={RelativeSource Self}}" Value="">
            <Setter Property="Text" Value="No value found" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Why does the DataContext fail to inherit to an object constructed directly as an Attached Property?

etberg
  • 111
  • 1
  • 7

1 Answers1

4

Why would it?

Let's remember that FrameworkElement.DataContext is implemented as a DependencyProperty with inheritance flag (see code source here, line 2704) and as such, the "inheritance" of the DataContext you are talking about takes place according to the documented inheritance rules:

Property value inheritance enables child elements in a tree of elements to obtain the value of a particular property from parent elements.

Property value inheritance is particularly about how property values can inherit from one element to another on the basis of the parent-child relationships within a tree of elements

In your case, there is no such parent-child relationship between your TextBlock and its AttachedProperty.FrameworkObject value, not in the WPF sense. The fact that it is an attached or not dependency property doesn't have an impact actually.


Some comments on your attempts

Creating the object as an element in the Visual Tree successfully binds the value

Yes, because then the object inherits the same DataContext as the TextBlock because they have the same parent in the element tree.

Creating the object within the scope of the TextBlock fails to bind properly

Yes, because the TextBlock is not a parent of the object in the element tree. The TextBlock just happens to hold a reference to that object (I'm hiding the complexity of attached properties here).

Using a data proxy will resolve the binding even when created in a control's scope

Yes, because the object is the child of a ContentControl itself a child of an element with the correct DataContext.

And the most confusing to me. Constructing the FrameworkObject within a ContentControl.Content seems to bind perfectly fine.

Same as above.


If you really need your object to have a DataContext in your second scenario, you have to set it yourself because it won't be naturally inherited:

<TextBlock x:Name="textBlock" Style="{StaticResource DisplayFromAttached}">
    <local:AttachedProperty.FrameworkObject>
        <local:FrameworkObject DataContext="{Binding ElementName=textBlock, Path=DataContext}" x:Name="CreatedInScope" MyDependencyProperty="{Binding MyProperty, diag:PresentationTraceSources.TraceLevel=High}" />
    </local:AttachedProperty.FrameworkObject>
</TextBlock>
Community
  • 1
  • 1
Corentin Pane
  • 4,794
  • 1
  • 12
  • 29
  • This is a satisfying answer. So if there is no direct ownership, the DataContext cannot inherit. The same reason that constructing this object in a Resource dictionary would fail unless it became the child of an element in the visual tree (e.g. Content). Question: If the Textblock is not the parent of the Attached Property, who is, if anyone? – etberg Feb 05 '20 at 16:10
  • 1
    No one, it is not in the visual tree. The same way the `TextBlock` is not the parent of its `Text` property of type `string`, it is not the parent of its `FrameworkObject` property of type `FrameworkObject`. Attached or not:) – Corentin Pane Feb 05 '20 at 16:15
  • So what *is* the basis for parent-child relationships, then? Is it exclusively delegated to Content within ContentControls and its derivatives? – etberg Feb 05 '20 at 16:20
  • 1
    It would be an hybrid between the logical tree and the visual tree, as explained [here](https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/trees-in-wpf#property-value-inheritance). In particular, it applies to ContentControl with Content, but also to ItemsControl with Items, Border with Child etc... – Corentin Pane Feb 06 '20 at 08:14
  • 1
    Thank you very much! Great answers all around! – etberg Feb 06 '20 at 15:42