WPF .NET Core - Inside Dependency Properties
Written by Alex Armstrong   
Thursday, 28 May 2020
Article Index
WPF .NET Core - Inside Dependency Properties
Why Bother?
Binding

Why use dependency properties?

What do you get extra with Dependency properties?

If you have used WPF controls you will know that most of the properties that they support are dependency properties and you will also probably know the sorts of things you can do with them.

The most obvious special feature of a dependency property is that it can join in with data binding. That is, a dependency property can be linked to another property such that a change in the value of one changes the other.

Notice that this sort of behaviour can be achieved using OnChange event handlers but databinding is completely automatic and doesn't require you to enable or handle events of any kind. In most cases if your purpose is to link two or more properties then data binding is the way to go.

In addition to databinding Dependency properties have other features - as the documentation says:

support for value expressions, property invalidation and dependent-value coercion, default values, inheritance, data binding, animation, property change notification, and styling.

All of these are created by virtue of the object nature of a dependency property, i.e. a dependency property is an object of type Dependency-Property that engages in all of the above behaviors. 

Each of these behaviors could occupy a complete article in its own right so to make things simpler let’s concentrate on data binding in the examples.

 

XAML

 

Dependency Property Basics

Now that we know the basic principle of the dependency property - it's an object oriented property - we can try creating a custom dependency property

The first complication is that we can't just create an instance of DependencyProperty the framework needs to know about it to allow it to take part in the common behaviours.

The solution employed by its designers is to use a static method of DependencyProperty to create an instance and register it:

public static readonly
  DependencyProperty MyDPProperty =
     DependencyProperty.Register(
                             "MyDP",
                             typeof(double),
                             typeof(MyClass));

The method call returns a DependencyProperty object set up to store a value of type double, with name MyDP and to act as a property belonging to MyClass.

This is how all dependency properties are created and they have to be public static readonly fields.

The reason is that the property belongs to the class and it’s the implementation that sorts out the instance being used. That is there is only one property object shared among all of the instances of the class that uses it. Which particular instance value the property is working with is determined by "this" at runtime.

There are a number of overloaded Register methods but at the very least you have to specify:

  • the name of the property as a string "MyDP" in the example

  • the type of the property  typeof(double) in the example

  • the type of the class the property belongs to typeof(MyClass) in the example.

Notice that the static readonly field is generally given the same name as the property but with "Property" tagged on.

Also notice that there are now two ways of referring to the property either by its name i.e. MyDP or by the variable MyDPProperty. You can also register a range of metadata values and callbacks but this is the minimum you need to create a custom dependency property.

Next we need the details of the class that the property belongs to. It can't be any class. It needs to inherit the methods needed to work with a dependency property and this means it has to inherit from DependencyObject or one of its descendants.

As C# only supports single inheritance you can see that this is quite a restriction and it is important that you choose the correct class to inherit from if you also want properties and methods of a particular control.

 

Banner

Custom Dependency Properties

With these thoughts in mind we can now put together a complete class definition:

public class MyClass:DependencyObject
{
 public static readonly
      DependencyProperty MyDPProperty
          = DependencyProperty.Register(
                              "MyDP",
                              typeof(double),
                              typeof(MyClass));
}

 

The class now has a static DependencyProperty as a field but we now have another small problem.

The DependencyObject provides a setter and getter method but these infer the instance of the class from the current context. That is:

GetValue(MyDPProperty)

and

SetValue(MyDPProperty, value)

use "this" to determine the instance of the property that they should get or set. If you think about this you will realise that this means that you can only get and set a dependency property within the class or object that it belongs to.

That is, an object cannot use them to access the dependency property on another object.

The solution is to use the dependency property to back a standard CLR property. For example:

public class MyClass:DependencyObject
{
 public double MyProp
 {
  get
  {
   return (double) GetValue(MyDPProperty);
  }
  set
  {
   SetValue(MyDPProperty, value);
  }
 }

 public static readonly 
      DependencyProperty MyDPProperty
          = DependencyProperty.Register(
                              "MyDP",
                              typeof(double),
                              typeof(MyClass));
}

 

Now when we assign or access MyProp it's actually MyDPProperty that is used.



Last Updated ( Thursday, 28 May 2020 )