86

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')
Taras Shcherban
  • 187
  • 2
  • 11
Daniel Bişar
  • 2,663
  • 7
  • 32
  • 54

3 Answers3

202

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".

Vimes
  • 10,577
  • 17
  • 66
  • 86
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • 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" – Daniel Bişar Apr 29 '11 at 13:07
  • 10
    The `RegisterAttached` call should pass "Test", not "TestProperty" as the property name. – Kent Boogaart Apr 29 '11 at 15:22
  • 2
    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. – tofutim May 15 '13 at 15:32
  • 9
    Why are the parentheses required? What reference is one supposed to read to know this arcane stuff? – xvan Jun 08 '17 at 18:59
  • 2
    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`. – aaronburro Aug 17 '18 at 17:40
  • 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. – aaronburro Aug 17 '18 at 17:40
  • Do these kind of bindings automatically refresh if the value of the attached property changes? – sa.he Oct 19 '21 at 12:37
22

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.

Livven
  • 7,841
  • 6
  • 25
  • 20
  • 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! – Robert Petz Dec 03 '13 at 21:16
  • Not sure why, but WPF crashes if I don't add Path= and I'm running WPF 4.5 I'm running Win 8.1 – Mo0gles Jan 16 '14 at 10:29
  • 3
    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. – Johan Appelgren Mar 03 '16 at 14:20
  • dude, you literally saved my day. Thanks a lot. I use .net 4.0 and without Path=(...) it doesn't work at all. – Vlad Sep 06 '17 at 11:35
  • 1
    .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? – Mios Dec 09 '21 at 23:45
-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)}" 
Ali
  • 2,702
  • 3
  • 32
  • 54