#574 – Complete Example of Implementing a Dependency Property

Recall that you can implement a dependency property if your class inherits from DependencyObject.  Here is a complete example, showing how to implement a dependency property.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;   // Remember to reference WindowsBase

namespace PersonDLL
{
    public class Person : DependencyObject
    {
        // Classic CLR properties
        public string Name { get; set; }
        public bool AARPCandidate { get; set; }

        // Age is a dependency property
        public static readonly DependencyProperty AgeProperty;

        // Call methods in DependencyObject to read/write property values
        public int Age
        {
            get { return (int)GetValue(AgeProperty); }
            set { SetValue(AgeProperty, value); }
        }

        // Static constructor sets everything up
        static Person()
        {
            PropertyMetadata ageMetadata =
                new PropertyMetadata(
                    18,     // Default value
                    new PropertyChangedCallback(OnAgeChanged),
                    new CoerceValueCallback(OnAgeCoerceValue));

            // Register the property
            AgeProperty =
                DependencyProperty.Register(
                    "Age",                 // Property's name
                    typeof(int),           // Property's type
                    typeof(Person),        // Defining class' type
                    ageMetadata,           // Defines default value & callbacks  (optional)
                    new ValidateValueCallback(OnAgeValidateValue));   // validation (optional)
        }

        // Value has changed
        private static void OnAgeChanged
            (DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            Person p = (Person)depObj;
            p.AARPCandidate = (int)e.NewValue > 60 ? true : false;
        }

        // Allow coercing value being set
        private static object OnAgeCoerceValue
            (DependencyObject depObj, object baseValue)
        {
            int coercedValue = (int)baseValue;

            if ((int)baseValue > 120)
                coercedValue = 120;

            if ((int)baseValue < 1)
                coercedValue = 1;

            return coercedValue;
        }

        // Validate a value beint set
        private static bool OnAgeValidateValue(object value)
        {
            int age = (int)value;

            // Only allow reasonable ages
            return (age > 0) && (age < 120);
        }
    }
}

#167 – Implementing a Dependency Property That Is A Collection

When you create a custom dependency property that is based on a collection type, you need to define the default value of the property as a new instance of a collection, rather than as just a static value.

For example, we defined the Person.IQ dependency property, with a default value of 100, as follows:

internal static readonly DependencyPropertyKey IQPropertyKey =
    DependencyProperty.RegisterReadOnly("IQ", typeof(int), typeof(Person), new PropertyMetadata(100));

If we define a read-only Person.Friends property, whose type is List<Person>, the same call would look like this:

        internal static readonly DependencyPropertyKey FriendsPropertyKey =
            DependencyProperty.RegisterReadOnly("Friends", typeof(List<Person>), typeof(Person),
              new PropertyMetadata(new List<Person>()));      // Metadata constructor instantiates a new List

#160 – Be Careful When Setting Dependency Property Values from a DependencyObject Constructor

You should normally avoid calling any virtual methods in a C# class constructor.  Your constructor might get called during construction of a derived class, before the derived class has finished initializing everything that it needs to initialize.  If the derived class also overrides the virtual method that you’re calling, that method could end up getting called before the derived class has finished initializing everything (in its constructor).

If you use FxCop to check your code, this will be caught by the DoNotCallOverridableMethodsInConstructors rule.

This rule applies to WPF.  If you set the value of a dependency property in a constructor, virtual methods or callbacks in the property system may end up getting called.

The best way to avoid the problem is to follow the following rule:

  • Initialize all objects that might be used by property system callbacks in your default (parameterless) constructor

See also: Safe Constructor Patterns for DependencyObjects

Follow

Get every new post delivered to your Inbox.

Join 233 other followers