WPF Dependency Properties

Posted by Al on Sun, April 17 at 14:50:04 in C#

Traditionally, a property in .NET is a member variable with associated get and set methods that are used to read and write its value.

Ex.Traditional .NET Property

private int mEmployeeRating;

 

public int EmployeeRating

{

    get { return mEmployeeRating; }

    set { mEmployeeRating = value; }

}

WPF introduces the concept of a dependency property, which also uses get and set methods to read and write its value, but instead of storing the property value in a member variable, instead stores its value in a dictionary maintained by the property system in WPF.  In other words the back store for a traditional property is a member variable and the back store for a dependency property is a dictionary maintained by the property system.  This gives the property system the ability to manipulate the value returned by the property.

So why would you want to use a dependency property?

Dependency properties give you additional functionality that traditional properties do not supply.  Dependency properties allow you to:

  • Set the property value using resources.
  • Bind the property to a data source, so that property value gets update dynamically whenever the data source value changes.
  • Set the property value using styles.
  • Set the property value dynamically when an animation is running.
  • Set the property value by overriding its metadata.
  • Set the property value by inheriting its value from a parent in the object tree.
  • Dependency properties also are natively supported by the WPF Designer in Visual Studio.
  • Attach callbacks that can be used to notify you and a dependency property value has changed or is about to be changed.

As you can see, the majority of functionality available with dependency properties are most useful for creating dynamic user interfaces, which is not surprising given they are part of WPF.

To support dependency properties, the object containing your property needs to derive from a DependencyObject.  This will hook your object into the WPF property system.

Next, add a static DependencyProperty member to your object and call the DependencyProperty.Register() method to register your new property.

Finally, add get and set accessors to your object to allow the outside world access to your property.

 

Ex. Creating a Dependency Property

public int EmployeeRating

{

    get { return (int)GetValue(EmployeeRatingProperty); }

    set { SetValue(EmployeeRatingProperty, value); }

}

 

public static readonly DependencyProperty EmployeeRatingProperty =

   DependencyProperty.Register("EmployeeRating", typeof(int), typeof(Employee),

   new UIPropertyMetadata(0));

If you are using Visual Studio, you can add your dependency property using Snippets, by selecting “Edit / Intellisense / Insert Snippet…”, and then selecting “NetFX30 / Define a DependencyProperty”

One thing to note;  you should never add any additional code to your get and set methods because the end user can access the property value using either the get and set methods or the intrinsic GetValue and SetValue methods and you want the behavior to stay the same no matter which mechanism is used.

In the example above, you should note that one of the parameters passed in during the registration of your property allows you to pass in a Metadata object.  You can use this metadata to set a default value for your property and to register callbacks that will notify you when a change is about to or has occurred on your property.  The callbacks available are as follows:

  • Value Changed Callback - this is called whenever the value of your property has been changed.
  • Coerce Value Callback - this gets called whenever your property is about to be changed and allows you modify the change value if you find it unacceptable.  This could be used to set minimum and maximum allowable values for your property.
  • Validation Callback - allows you to check the to see if the newly assigned value is valid.  If it is, return true, if not return false and an ArgumentExeception will be thrown.

 

Ex. Creating a Dependency Property with Metadata

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

 

namespace DepPropEx

{

    class Employee : DependencyObject

    {

        const int MAX_RATING = 10;

        const int MIN_RATING = 0;

 

        public int EmployeeRating

        {

            get { return (int)GetValue(EmployeeRatingProperty); }

            set { SetValue(EmployeeRatingProperty, value); }

        }

 

        public static readonly DependencyProperty EmployeeRatingProperty =

            DependencyProperty.Register("EmployeeRating", typeof(int), typeof(Employee),

            new PropertyMetadata(MIN_RATING, EmployeeRatingChangedCallback, EmployeeRatingCoerceCallback), EmployeeRatingValidateCallback);

 

        private static void EmployeeRatingChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)

        {

            MessageBox.Show(String.Format("The EmployeeRating changed from {0} to {1}", e.OldValue, e.NewValue));

        }

 

        // Return mLastEmployeeRatingValue + 1 if the value falls outside the range of 1000 to 10000

        private static object EmployeeRatingCoerceCallback(DependencyObject obj, object o)

        {

            if ((int)o > MAX_RATING)

                return MAX_RATING;

 

            return o;

        }

 

        // If the new value is the same as the old value force an exception to be thrown.

        private static bool EmployeeRatingValidateCallback(object value)

        {

            if ((int)value < MIN_RATING)

                return false;

 

            return true;

        }

    }

}

 

 

Posted by Al on 04/17/11 at 02:50 PM in C#

Leave a Comment

Name:

Email:

Location:

URL:

Notify me of follow-up comments?

Submit the word you see below:


Comments