What's the difference between a dependency property and an attached property in WPF?
Asked Answered
I

5

100

What's the difference between a (custom) dependency property and an attached property in WPF? What are the uses for each? How do the implementations typically differ?

Inlaw answered 6/8, 2009 at 18:52 Comment(0)
R
39

Abstract

Since I found little to no documentation on the matter, it took some poking around the source code, but here's an answer.

There is a difference between registering a dependency property as a regular and as an attached property, other than a "philosophical" one (regular properties are intended to be used by the declaring type and its deriving types, attached properties are intended to be used as extensions on arbitrary DependencyObject instances). "Philosophical", because, as @MarqueIV noticed in his comment to @ReedCopsey's answer, regular properties can also be used with arbitrary DependencyObject instances.

Moreover, I have to disagree with other answers stating that attached property is "type of dependency property", because it's misleading - there aren't any "types" of dependency properties. The framework doesn't care if the property was registered as attached or not - it's not even possible to determine (in the sense that this information is not recorded, because it's irrelevant). In fact, all properties are registered as if they were attached properties, but in case of regular ones some additional things are done that slightly modify their behavior.

Code excerpt

To save you the trouble of going through the source code yourself, here's a boiled down version of what happens.

When registering a property without metadata specified, calling

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

yields exactly the same result as calling

DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

However, when specifying metadata, calling

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

is equivalent to calling

var property = DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    defaultMetadata: new PropertyMetadata
    {
        DefaultValue = "default value",
    });
property.OverrideMetadata(
    forType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

Conclusions

The key (and only) difference between regular and attached dependency properties is the default metadata available through DependencyProperty.DefaultMetadata property. This is even mentioned in the Remarks section:

For nonattached properties, the metadata type returned by this property cannot be cast to derived types of PropertyMetadata type, even if the property was originally registered with a derived metadata type. If you want the originally registered metadata including its original possibly derived metadata type, call GetMetadata(Type) instead, passing the original registering type as a parameter.

For attached properties, the type of the metadata returned by this property will match the type given in the original RegisterAttached registration method.

This is clearly visible in the provided code. Little hints are also hidden in the registering methods, i.e. for RegisterAttached the metadata parameter is named defaultMetadata, whereas for Register it is named typeMetadata. For attached properties the provided metadata becomes the default metadata. In case of regular properties however, the default metadata is always a fresh instance of PropertyMetadata with only DefaultValue set (either from provided metadata or automatically). Only the subsequent call to OverrideMetadata actually uses the provided metadata.

Consequences

The main practical difference is that in case of regular properties the CoerceValueCallback and PropertyChangedCallback are applicable only for types derived from the type declared as the owner type, and for attached properties they're applicable for all types. E.g. in this scenario:

var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");

the registered PropertyChangedCallback will be called if the property was registered as an attached property, but will not be called if it was registered as a regular property. Same goes to CoerceValueCallback.

A secondary difference stems from the fact that OverrideMetadata requires that supplied type derives from DependencyObject. In practice it means that the owner type for regular properties must derive from DependencyObject, whereas for attached properties in can be any type (including static classes, structs, enums, delegates, etc.).

Supplement

Besides @MarqueIV's suggestion, on several occasions I've come across opinions that regular and attached properties differ in the way they can be used in XAML. Namely, that regular properties require implicit name syntax as opposed to explicit name syntax required by attached properties. This is technically not true, although in practice it usually is the case. For clarity:

<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" /> 

<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />

In pure XAML, the only rules governing the usage of these syntaxes are the following:

  • Implicit name syntax can be used on an element if and only if the class that this element represents has a CLR property of that name
  • Explicit name syntax can be used on an element if and only if the class specified by the first part of the full name exposes appropriate static get/set methods (referred to as accessors) with names matching the second part of the full name

Satisfying these conditions enables you to use corresponding syntax regardless of whether the backing dependency property was registered as regular or attached.

Now the mentioned misconception is caused by the fact that vast majority of tutorials (together with stock Visual Studio code snippets) instruct you to use CLR property for regular dependency properties, and get/set accessors for attached ones. But there's nothing stopping you from using both at the same time, allowing you to use whichever syntax you prefer.

Rollo answered 9/5, 2018 at 16:7 Comment(4)
This is one of the best SO answers I've ever read. If you write a book, I'll buy it.Andreasandree
Are you sure that "it's not even possible to determine (in the sense that this information is not recorded)." ? I was wondering because I just discovered MarkupProperty.IsAttached and DependencyPropertyDescriptor.IsAttached. Do you have any information about these?Dynamiter
@Dynamiter I've never seen these before. I'm not sure what MarkupProperty class is used for, so I'm not able to give you a definitive answer at this time. My first impression is that it represents some kind of metadata used when serializing an object to markup (XAML?), including what syntax (attached/non-attached) to use to write particular properties. I'm not sure however if it's 100% accurate. I'm not even sure how to define a non-attached property - if you do not actually call the Register method, but do everything it does yourself, would you consider such property non-attached or not?Rollo
[vast majority of tutorials instruct you to use CLR property for regular dependency properties, and get/set accessors for attached ones] I think this is because attached properties are usually used with static classes where you cannot have CRL properties as you need to pass DependencyObject to call SetValue/GetValue from them.Kowtko
C
75

Attached properties are a type of dependency property. The difference is in how they're used.

With an attached property, the property is defined on a class that isn't the same class for which it's being used. This is usually used for layout. Good examples are Panel.ZIndex or Grid.Row - you apply this to a control (ie: Button), but it's actually defined in Panel or Grid. The property is "attached" to the button's instance.

This allows a container, for example, to create properties that can be used on any UIelement.

As for implementation differences - it's basically just a matter of using Register vs. RegisterAttached when you define the property.

Coordination answered 6/8, 2009 at 18:56 Comment(1)
But what exactly is the difference?! From what I've seen you can attach a non-attachable property to another via code (I think this is blocked in XAML though.) Perhaps that's the difference?Flossi
R
39

Abstract

Since I found little to no documentation on the matter, it took some poking around the source code, but here's an answer.

There is a difference between registering a dependency property as a regular and as an attached property, other than a "philosophical" one (regular properties are intended to be used by the declaring type and its deriving types, attached properties are intended to be used as extensions on arbitrary DependencyObject instances). "Philosophical", because, as @MarqueIV noticed in his comment to @ReedCopsey's answer, regular properties can also be used with arbitrary DependencyObject instances.

Moreover, I have to disagree with other answers stating that attached property is "type of dependency property", because it's misleading - there aren't any "types" of dependency properties. The framework doesn't care if the property was registered as attached or not - it's not even possible to determine (in the sense that this information is not recorded, because it's irrelevant). In fact, all properties are registered as if they were attached properties, but in case of regular ones some additional things are done that slightly modify their behavior.

Code excerpt

To save you the trouble of going through the source code yourself, here's a boiled down version of what happens.

When registering a property without metadata specified, calling

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

yields exactly the same result as calling

DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

However, when specifying metadata, calling

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

is equivalent to calling

var property = DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    defaultMetadata: new PropertyMetadata
    {
        DefaultValue = "default value",
    });
property.OverrideMetadata(
    forType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

Conclusions

The key (and only) difference between regular and attached dependency properties is the default metadata available through DependencyProperty.DefaultMetadata property. This is even mentioned in the Remarks section:

For nonattached properties, the metadata type returned by this property cannot be cast to derived types of PropertyMetadata type, even if the property was originally registered with a derived metadata type. If you want the originally registered metadata including its original possibly derived metadata type, call GetMetadata(Type) instead, passing the original registering type as a parameter.

For attached properties, the type of the metadata returned by this property will match the type given in the original RegisterAttached registration method.

This is clearly visible in the provided code. Little hints are also hidden in the registering methods, i.e. for RegisterAttached the metadata parameter is named defaultMetadata, whereas for Register it is named typeMetadata. For attached properties the provided metadata becomes the default metadata. In case of regular properties however, the default metadata is always a fresh instance of PropertyMetadata with only DefaultValue set (either from provided metadata or automatically). Only the subsequent call to OverrideMetadata actually uses the provided metadata.

Consequences

The main practical difference is that in case of regular properties the CoerceValueCallback and PropertyChangedCallback are applicable only for types derived from the type declared as the owner type, and for attached properties they're applicable for all types. E.g. in this scenario:

var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");

the registered PropertyChangedCallback will be called if the property was registered as an attached property, but will not be called if it was registered as a regular property. Same goes to CoerceValueCallback.

A secondary difference stems from the fact that OverrideMetadata requires that supplied type derives from DependencyObject. In practice it means that the owner type for regular properties must derive from DependencyObject, whereas for attached properties in can be any type (including static classes, structs, enums, delegates, etc.).

Supplement

Besides @MarqueIV's suggestion, on several occasions I've come across opinions that regular and attached properties differ in the way they can be used in XAML. Namely, that regular properties require implicit name syntax as opposed to explicit name syntax required by attached properties. This is technically not true, although in practice it usually is the case. For clarity:

<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" /> 

<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />

In pure XAML, the only rules governing the usage of these syntaxes are the following:

  • Implicit name syntax can be used on an element if and only if the class that this element represents has a CLR property of that name
  • Explicit name syntax can be used on an element if and only if the class specified by the first part of the full name exposes appropriate static get/set methods (referred to as accessors) with names matching the second part of the full name

Satisfying these conditions enables you to use corresponding syntax regardless of whether the backing dependency property was registered as regular or attached.

Now the mentioned misconception is caused by the fact that vast majority of tutorials (together with stock Visual Studio code snippets) instruct you to use CLR property for regular dependency properties, and get/set accessors for attached ones. But there's nothing stopping you from using both at the same time, allowing you to use whichever syntax you prefer.

Rollo answered 9/5, 2018 at 16:7 Comment(4)
This is one of the best SO answers I've ever read. If you write a book, I'll buy it.Andreasandree
Are you sure that "it's not even possible to determine (in the sense that this information is not recorded)." ? I was wondering because I just discovered MarkupProperty.IsAttached and DependencyPropertyDescriptor.IsAttached. Do you have any information about these?Dynamiter
@Dynamiter I've never seen these before. I'm not sure what MarkupProperty class is used for, so I'm not able to give you a definitive answer at this time. My first impression is that it represents some kind of metadata used when serializing an object to markup (XAML?), including what syntax (attached/non-attached) to use to write particular properties. I'm not sure however if it's 100% accurate. I'm not even sure how to define a non-attached property - if you do not actually call the Register method, but do everything it does yourself, would you consider such property non-attached or not?Rollo
[vast majority of tutorials instruct you to use CLR property for regular dependency properties, and get/set accessors for attached ones] I think this is because attached properties are usually used with static classes where you cannot have CRL properties as you need to pass DependencyObject to call SetValue/GetValue from them.Kowtko
G
5

Attached properties are basically meant for the container elements.like if you have a grid and you have grid.row now this is considered to be an attached property of a grid element.also you can use this property in texbox,button etc to set its place in the grid.

Dependency property is like the property basically belongs to some other class and is used in other class. eg: like you have a rectangle here height and width are regular properties of rectangle,but left and top are the dependency property as it belongs to Canvass class.

Gilbertina answered 10/1, 2012 at 11:33 Comment(0)
T
0

Attached properties are a special kind of DependencyProperties. They allow you to attach a value to an object that does not know anything about this value. A good example for this concept are layout panels. Each layout panel needs different data to align its child elements. The Canvas needs Top and Left, The DockPanel needs Dock, etc. Since you can write your own layout panel, the list is infinite. So you see, it's not possible to have all those properties on all WPF controls. The solution are attached properties. They are defined by the control that needs the data from another control in a specific context. For example an element that is aligned by a parent layout panel.

Tansey answered 15/7, 2016 at 6:3 Comment(0)
A
-1

I think you can defined attached property in the class itself or you can define it in another class. We always could use attached property to extend standard microsoft controls. But dependency property, you define it in your own custom control. e.g. You can inherit your control from a standard control, and define a dependency property in your own control and use it. This is equivalent to define an attached property, and use this attached property in the standard control.

Arius answered 12/6, 2017 at 20:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.