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:
ViewModel
, with a property, ViewModel.MyProperty
of type string set to "123456789abc"
class FrameworkObject : FrameworkElement
- no UI defined, but has a DataContext. This object has a defined dependency property, string FrameworkObject.MyDependencyProperty
AttachedProperty.FrameworkObject
, which takes an object of type FrameworkObject
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?
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>