wpfdependency-properties

WPF Bind to an attached property somewhere up in the visual tree


I have an attached property ZoneBackground. This can be noted on any framework element.

I now have a style ZonedTextBox. This should apply the value of ZoneBackground to a textbox. The clue is: The style does not know where in the visual hierachy ZoneBackground is noted and on which element.

Is it possible to search for the first parent which has a value for ZoneBackground and use that value?

I got this XAML:

<Grid controls:ZoneStylingBehavior.ZoneBackground="Red">
    ...
    <TextBox Background="{Binding controls:ZoneStylingBehavior.ZoneBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}" />
    ...
</Grid>

This does not work and I would further more like to be able to note ZoneBackground anywhere in the visual tree like on StackPanel, Grid and maybe multiple times.


Update: As suggested I tried it with dp inheritance. That idea sounds great but I was not yet able to succeed.

The DP declaration:

public static readonly DependencyProperty ZoneBackgroundProperty = DependencyProperty.RegisterAttached("ZoneBackground", typeof(Brush), typeof(ZoneStylingBehavior), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));

The XAML:

<StackPanel>
    <Grid controls:ZoneStylingBehavior.ZoneBackground="{StaticResource BrushGreen}">
         ...
         <Label Background="{Binding controls:ZoneStylingBehavior.ZoneBackground, RelativeSource={RelativeSource Self}, PresentationTraceSources.TraceLevel=High}" Content="test" />
         ...
    </Grid>
    ...
    <Grid controls:ZoneStylingBehavior.ZoneBackground="{StaticResource BrushRed}">
        ...
    </Grid>
</StackPanel>

The console outputs:

System.Windows.Data Error: 40 : BindingExpression path error: 'controls:ZoneStylingBehavior' property not found on 'object' ''TextBox' (Name='')'. BindingExpression:Path=controls:ZoneStylingBehavior.ZoneBackground; DataItem='TextBox' (Name=''); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')

The binding trace on level High shows:

System.Windows.Data Warning: 56 : Created BindingExpression (hash=17086942) for Binding (hash=33055417)
System.Windows.Data Warning: 58 :   Path: 'controls:ZoneStylingBehavior.ZoneBackground'
System.Windows.Data Warning: 60 : BindingExpression (hash=17086942): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=17086942): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=17086942): Attach to System.Windows.Controls.Label.Background (hash=18524697)
System.Windows.Data Warning: 67 : BindingExpression (hash=17086942): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=17086942): Found data context element: <null> (OK)
System.Windows.Data Warning: 72 :   RelativeSource.Self found Label (hash=18524697)
System.Windows.Data Warning: 78 : BindingExpression (hash=17086942): Activate with root item Label (hash=18524697)
System.Windows.Data Warning: 108 : BindingExpression (hash=17086942):   At level 0 - for Label.controls:ZoneStylingBehavior found accessor <null>
System.Windows.Data Error: 40 : BindingExpression path error: 'controls:ZoneStylingBehavior' property not found on 'object' ''Label' (Name='')'. BindingExpression:Path=controls:ZoneStylingBehavior.ZoneBackground; DataItem='Label' (Name=''); target element is 'Label' (Name=''); target property is 'Background' (type 'Brush')
System.Windows.Data Warning: 103 : BindingExpression (hash=17086942): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 80 : BindingExpression (hash=17086942): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=17086942): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=17086942): TransferValue - using final value <null>

Can someone point out what's wrong?


Solution

  • Instead of searching first parent in Visual tree where this value is set, make the attached property to inherit by default by setting FrameworkPropertyMetadataOptions.Inherits flag for attached property while registering it.

    Sample:

    public static readonly DependencyProperty ZoneBackgroundProperty =
             DependencyProperty.RegisterAttached("ZoneBackground",
                                          typeof(Brush), typeof(ZoneStylingBehavior),
           new FrameworkPropertyMetadata(FrameworkPropertyMetadataOptions.Inherits));
    

    So, this way it will automatically inherit value from its first parent in Visual tree with value set for attached property. (DataContext DP also works like this)

    UPDATE:

    For attached property binding you need to wrap binding in parenthesis.

    <Label Background="{Binding (controls:ZoneStylingBehavior.ZoneBackground), 
                                RelativeSource={RelativeSource Self}}"
           Content="test"/>