Get up to 40 % extra points for free! More info
Save up to 80 % on our Swift e-learning courses. Only this week!

Lesson 33 - DependencyProperties in C# .NET WPF

In first WPF lessons, we've learned about principles of data binding and the INotifyPropertyChanged interface. We know that these are powerful tools and are even necessary to build more robust applications. Otherwise, it'd be very difficult to manually make sure form data are up-to-date unlike data in an application.

In addition to INotifyPropertyChanged, WPF has another tool that can also automatically respond to value changes of a property. This tool is called Dependency Properties.

Dependency Properties

WPF went for it from scratch and came up with an innovation of C# properties themselves. The concept of Dependency Properties is much more complex and powerful.

All WPF controls are built on Dependency Properties internally. That's so they can support data binding even in the opposite direction. This way it's internally possible that a TextBlock changes its Text, which is a DependencyProperty and not a common CLR property, as we've probably thought so far.

Until we create our own WPF control, Dependency Properties don't really bring much new to us, and its syntax can be confusing for other programmers. The usage of Dependency Properties in code we write and that isn't our own WPF element, e.g. in ViewModels, is a relatively controversial topic and there are long discussions about it. As the result, using Dependency Properties it can be a bit faster and it allows you to animate values, for example. In today's lesson we'll introduce this technology mainly because WPF is built on it internally and it's therefore a basic knowledge. You can also come across them in other source code. But that doesn't mean you have to start using them everywhere, instead of how we've programmed so far.

A classic property vs. DependencyProperty

Let's compare known implementations of properties and then show the new one.

A classic property

Everything is fine here:

public class User
{
    public int Age { get; set; }
}

A classic property for data binding

For example, if we wanted to bind a form to a property, we'd have to implement the INotifyPropertyChanged interface in the class (or INotifyCollectionChanged if the class represented a collection). Only then did the control, which was bound to the property, find out that its value had changed. Here we need to store the data in some private attribute of the class (typically with the same name as the property, but with a lowercase letter or underscore):

public class User: INotifyPropertyChanged
{
    private int age;
    public int Age
    {
        get => age;
        set
        {
            OnPropertyChanged(nameof(Age));
        }
    }

    private void OnPropertyChanged(string property) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

}

An internal attribute is a backing field of its property. .[hint]

In the setter, we call the recovery method, which when the property changes, invokes an appropriate event, to which other parts of the application can automatically respond. Still nothing new.

DependencyProperty

To store a value of a DependencyProperty we no longer need a common attribute of the given type, but we'll find it out dynamically. Therefore, a value doesn't have to be stored at all and some default one can be used, which reduces memory usage of the application. It's no longer the case that each property has one stored value. WPF has found out that after all we don't change a font, color, shadow, etc. of controls... So why should each button store a value in its attribute, when we can simply return the same default value in the getter of all buttons?

We use a static instance of the DependencyProperty class to get a value. We then find the value by calling the GetValue() method of the DependencyObject class, from which each class with a DependencyProperty must inherit. And this dependency is one of the reasons why we shouldn't use DependencyPro­perties in ViewModels. Whereas UserControl, an ancestor of its WPF elements, already has the DependencyObject class inherited. We use the analog method SetValue() to set a value.

Let's try it. We'll add a new UserControl and we'll create a City property in its Code Behind:

public static readonly DependencyProperty CityProperty =
    DependencyProperty.Register("City", typeof(string), typeof(ClassType));

public string City
{
    get { return (string)GetValue(CityProperty); }
    set { SetValue(CityProperty, value); }
}

First we added a static attribute of the DependencyProperty type, initialized by the static Register() method. The name DependencyProperty should always end with the word Property, it's the naming convention in WPF.

 Therefore, the property's backing field is here a static DependencyProperty instance and this way are default values shared across instances.

In order for a value to be accessible as a normal .NET property, we must add not only a DependencyProperty instance, but a classic property too. It does nothing but gets and sets values from its DependencyProperty using the GetValue() and SetValue() methods inherited from DependencyObject.

Important: We don't add any logic to these properties, as they're called only when we set a property from code. If we set a property from XAML, the SetValue() method is called directly.

We replace the ClassType type with our class type or the type of a class to which the property logically belongs.

Such a property doesn't need help in the form of a manual implementation of INotifyPropertyChanged, even if its declaration is similarly complex.

Note that the CityProperty attribute is really static. When we set the value of the property implemented as DependencyProperty, it won't be stored in the instance attribute of our instance, but in the dictionary of keys and values provided by the DependencyObject class. The key of the item is the name of the property and the value is then the value we want to set.

Benefits of DependencyPro­perties

There are several benefits of this new property type.

Lower memory requirements

We've already mentioned that we don't have to store all values of each control, but only those that have different values from default values. E.g. a row of buttons where the last one is blue will therefore be stored in the memory as:

  • default properties, including a color, which will be saved only once and used by all buttons
  • a blue color for the last button

Inheritance of values

If no local value is set, a DependencyProperty traverses the ancestor tree up until it finds a value, which a property then returns it.

Change notification

As we've already said, perhaps the most tempting reason to consider Dependency Properties is the built-in mechanism for notifying value changes, e.g. to restore a property print somewhere on a form without having to call it manually. By registering a callback in the property metadata, you receive a notification when a property's value changes. So it works on both sides.

In the next lesson, Our own control with DependencyProperties in C# .NET WPF , we'll program an example of using DependencyPro­perties in WPF in our own UserControl.


 

Previous article
WPF - Container Controls
All articles in this section
Form Applications in C# .NET WPF
Article has been written for you by Filip Smolík
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
Activities (2)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!