WPF Attached Property Data Binding
Asked Answered
I

3

87

I try to use binding with an attached property. But can't get it working.

public class Attached
{
    public static DependencyProperty TestProperty =
        DependencyProperty.RegisterAttached("TestProperty", typeof(bool), typeof(Attached),
        new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Inherits));

    public static bool GetTest(DependencyObject obj)
    {
        return (bool)obj.GetValue(TestProperty);
    }

    public static void SetTest(DependencyObject obj, bool value)
    {
        obj.SetValue(TestProperty, value);
    }
}

The XAML Code:

<Window ...>
    <StackPanel local:Attached.Test="true" x:Name="f">
        <CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}" />
        <CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay}" />
    </StackPanel>
</Window>

And the Binding Error:

System.Windows.Data Error: 40 : BindingExpression path error: '(local:Attached.Test)' property not found on 'object' ''StackPanel' (Name='f')'. BindingExpression:Path=(local:Attached.Test); DataItem='StackPanel' (Name='f'); target element is 'CheckBox' (Name=''); target property is 'IsChecked' (type 'Nullable`1')
Izettaizhevsk answered 29/4, 2011 at 12:45 Comment(0)
D
208

Believe it or not, just add Path= and use parenthesis when binding to an attached property:

IsChecked="{Binding Path=(local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}"

In addition, your call to RegisterAttached should pass in "Test" as the property name, not "TestProperty".

Discussion answered 29/4, 2011 at 12:48 Comment(8)
I already tried this and got an exception: Der Eigenschaftspfad ist ungültig. \"Attached\" besitzt keine öffentliche Eigenschaft mit dem Namen \"Test\". --> Engl: The Propertypath is invalid. "Attached" doesn't own a public property "Test"Melodeemelodeon
The RegisterAttached call should pass "Test", not "TestProperty" as the property name.Discussion
I wish i could upvote this more times. Make sure you rebuild the project after you make this change. I forgot to do it the first time and thought that Path= didn't actually work.Taipan
Why are the parentheses required? What reference is one supposed to read to know this arcane stuff?Janey
The parentheses just an "indexer" into the attached property system. It's how WPF distinguishes between attached properties and direct properties. I imagine it would be too confusing to the compiler to try and figure out which properties are attached and which ones are not, so using the parentheses provides that hint. If you are curious, under the hood, WPF actually creates a PropertyPath for an attached property as follows: new PropertyPath("(0)", new object[] { propertyName }), assuming propertyName is a String.Ejaculation
The 0 gets replaced with your property name, similar to String.Format usage, except the parentheses stay. What this says is to start looking for an attached property with appropriate name at the current data context. If you wanted to go hogwild, you can do attached properties off of other properties, such as: "{Binding Foo.(ClassProperty.Name).Baz}", which would then look for the ClassProperty.Name attached property starting off of the Foo property on your data context, and ultimately bind to the Baz property from the ClassProperty.Name attached property's value.Ejaculation
Do these kind of bindings automatically refresh if the value of the attached property changes?Mencher
Thank you so much for this answer! All this time, not adding the "Path=" has been jamming me up on attached behaviors.Wallpaper
N
24

I'd have preferred to post this as a comment on Kent's answer but since I don't have enough rep to do so... just wanted to point out that as of WPF 4.5, adding Path= isn't necessary anymore. However the attached property name still needs to be wrapped with parentheses.

Nonperformance answered 12/4, 2013 at 15:35 Comment(5)
was just about to post this as a comment myself, didn't realize that this is a WPF 4.5 and up feature...good to know, thanks!Reinareinald
Not sure why, but WPF crashes if I don't add Path= and I'm running WPF 4.5 I'm running Win 8.1Browband
Even with .NET 4.5 or .NET 4.6 I can't get it to work without Path= when binding to attached properties in DataTriggers.Microelectronics
dude, you literally saved my day. Thanks a lot. I use .net 4.0 and without Path=(...) it doesn't work at all.Saith
.NET 4.8.2 here - for some bizzare reasons, binding to attached properties in some nested custom controls works in some cases, but doesn't always. Adding Path= just solves problem I couldn't solve for 10h. Messages from PresentationTraceSources like ...property not found... are also gone. Bloody WPF magic. I will always use Path= whenever binding to attached properties in control templates. Maybe its because without the Path, Binding sets the Path trough in binding constructor rather in Path property setter?Lavelle
F
-3

Putting a bracket works. I had to do automation id binding of a parent contentcontrol to a textblock in datatemplate. Automation Id is an attached property.

I put the property in brackets and binding worked.

AutomationProperties.AutomationId="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=(AutomationProperties.AutomationId)}" 
Fleuron answered 10/1, 2019 at 5:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.