Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I am going over the differences between regular dependency properties and attached properties.
Using ILSpy, I had a look at how Register and RegisterAttached are implemented.
Register:
// System.Windows.DependencyProperty
/// <summary>Registers a dependency property with the specified property name, property type, owner type, property metadata, and a value validation callback for the property. </summary>
/// <returns>A dependency property identifier that should be used to set the value of a public static readonly field in your class. That identifier is then used to reference the dependency property later, for operations such as setting its value programmatically or obtaining metadata.</returns>
/// <param name="name">The name of the dependency property to register.</param>
/// <param name="propertyType">The type of the property.</param>
/// <param name="ownerType">The owner type that is registering the dependency property.</param>
/// <param name="typeMetadata">Property metadata for the dependency property.</param>
/// <param name="validateValueCallback">A reference to a callback that should perform any custom validation of the dependency property value beyond typical type validation.</param>
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
DependencyProperty.RegisterParameterValidation(name, propertyType, ownerType);
PropertyMetadata defaultMetadata = null;
if (typeMetadata != null && typeMetadata.DefaultValueWasSet())
defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);
DependencyProperty dependencyProperty = DependencyProperty.RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
if (typeMetadata != null)
dependencyProperty.OverrideMetadata(ownerType, typeMetadata);
return dependencyProperty;
RegisterAttached:
// System.Windows.DependencyProperty
/// <summary>Registers an attached property with the specified property type, owner type, property metadata, and value validation callback for the property. </summary>
/// <returns>A dependency property identifier that should be used to set the value of a public static readonly field in your class. That identifier is then used to reference the dependency property later, for operations such as setting its value programmatically or obtaining metadata.</returns>
/// <param name="name">The name of the dependency property to register.</param>
/// <param name="propertyType">The type of the property.</param>
/// <param name="ownerType">The owner type that is registering the dependency property.</param>
/// <param name="defaultMetadata">Property metadata for the dependency property. This can include the default value as well as other characteristics.</param>
/// <param name="validateValueCallback">A reference to a callback that should perform any custom validation of the dependency property value beyond typical type validation.</param>
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
DependencyProperty.RegisterParameterValidation(name, propertyType, ownerType);
return DependencyProperty.RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
It looks like the only difference is how the PropertyMetaData
is handled. Register has some extra logic using the passed in typeMetaData
parameter while RegisterAttached just passes the PropertyMetaData
along. Both methods then call RegisterCommon
.
When it comes to implementing attached properties, is this the only difference? I looked at the Set/GetValue methods on DependencyObject
. They are quite complicated (so I very easily could have missed some key bit of code that would explain things) but I couldn't find anything that would cause different code paths to be taken depending on what Register or RegisterAttached did.
Question:
Can someone explain to me what the difference I described above between Register and RegisterAttached is actually doing and how it allows attached properties to be used as a type of global property that is settable on any object? Maybe the place where the actual work is done is in the Set/GetValue methods on DependencyObject
?
–
TLDR: the differences you see in your code example are indeed the only differences between "attached" and regular dependency properties, and after that code is run they are treated the same by WPF, there is no such concept as "attached" property internally at all (like some flag DependencyProperty.IsAttached
or anything like that).
You are right that the difference between "attached" and regular dependency properties is just metadata handling.
First, it's perfectly possible to use regular property as attached one. For example suppose we have following property (note it's not "attached" in a sense RegisterAttached
is not used):
public class TestProperties {
public static readonly DependencyProperty TestProperty = DependencyProperty.Register(
"Test",
typeof(Boolean),
typeof(TestProperties)
public static void SetTest(UIElement element, Boolean value) {
element.SetValue(TestProperty, value);
public static Boolean GetTest(UIElement element) {
return (Boolean) element.GetValue(TestProperty);
And suppose we have TextBlock
like this:
<TextBlock x:Name="tb" local:TestProperties.Test="True" />
Because we have correct GetTest
and SetTest
methods - it's valid syntax. Now we can get the value of our properties as usual for attached properties:
// this returns true
var value = tb.GetValue(TestProperties.TestProperty);
Note that those GetTest
and SetTest
are not required either. They are needed only to be able to get\set "attached" property value from xaml. So far as you see regular and "attached" properties are exactly the same, there is no difference whatoever.
When we start to pass metadata - there are some differences though. When you register attached property, metadata you pass will be the default one and will apply to all types.
public class TestProperties {
public static readonly DependencyProperty TestProperty = DependencyProperty.RegisterAttached(
"Test",
typeof(Boolean),
typeof(TestProperties),
new PropertyMetadata(true, OnTestChanged)
private static void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
public static void SetTest(UIElement element, Boolean value) {
element.SetValue(TestProperty, value);
public static Boolean GetTest(UIElement element) {
return (Boolean) element.GetValue(TestProperty);
If we now do:
var meta = TestProperties.TestProperty.GetMetadata(typeof(TextBlock));
// or any other type
We will see that metadata for any type is the default one you passed, with callback and other stuff. What that means, for example, is your value changed callback will be called whenever value of your attached property changes for any type (like TextBlock
in our example above).
When we register property with metadata with regular Register
- it behaves differently:
PropertyMetadata defaultMetadata = null;
if (typeMetadata != null && typeMetadata.DefaultValueWasSet())
defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);
DependencyProperty dependencyProperty = DependencyProperty.RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
First it copies the default value (if any) and creates new metadata with only the default value. Then it creates property with that metadata but only default value is part of default metadata now. All callbacks are not part of default metadata. What that means is that now your callback will not be called when value of dependency property changes on arbitrary type (though default value is still preserved).
if (typeMetadata != null)
dependencyProperty.OverrideMetadata(ownerType, typeMetadata);
Now it takes full metadata you passed (with callbacks) and registers it only for the type you passed. In our example that means callbacks will be called only when value changes for type TestProperties
itself (which must inherit DependencyObject
from now on:
public class TestProperties : DependencyObject { // must inherit dependency object
public static readonly DependencyProperty TestProperty = DependencyProperty.Register(
"Test",
typeof(Boolean),
typeof(TestProperties),
new PropertyMetadata(true, OnTestChanged)
private static void OnTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
// this will be called ONLY when we do something like
// var prop = new TestProperties();
// prop.SetValue(TestProperty, true);
// but will NOT be called when we set value for TextBlock
public static void SetTest(UIElement element, Boolean value) {
element.SetValue(TestProperty, value);
public static Boolean GetTest(UIElement element) {
return (Boolean) element.GetValue(TestProperty);
If we want callback to be called for TextBlock - we can add that manually:
static TestProperties() {
TestProperties.TestProperty.OverrideMetadata(typeof(TextBlock), new PropertyMetadata(OnTestChanged));
But we have lost the (very useful) ability of "attached" properties to be able to track changes to values defined on any types (TextBlock
, Button
, whatever).
I hope this answers your question.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.