#1,040 – An Example of Dependency Property Inheritance

One of the attributes of dependency properties in WPF is that they support inheritance of property values within a tree of user interface elements.  Controls (dependency objects) within the tree that make use of a particular dependency property can set a property value locally or retrieve a value from an element higher up in the tree.  The local value takes precedence over the inherited value and then in turn applies to all elements “further down”.

(There are a number of other places from which a dependency property can get its value).

Below is an example.  FontSize is set to 20 for the top-level Window.  This value is use for lower-level elements unless they set their own value.  The GroupBox sets FontSize to 14, which then applies to it and elements under it.  Within the GroupBox, the second Label sets its own value for FontSize.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Dependency Properties" 
        Width="280" Height="252"
        FontSize="20">

    <StackPanel Name="spOuter" 
                Margin="5">
        <Label Name="lbl1" Content="Label 1"/>
        <Label Name="lbl2" Content="Label 2"/>
        <GroupBox Name="gb1" Header="Stuff" FontSize="14">
            <StackPanel Name="spInner" Orientation="Horizontal">
                <Label Name="lbl3" Content="Label 3"/>
                <Label Name="lbl4" Content="Label 4" FontSize="10"/>
            </StackPanel>
        </GroupBox>
        <Button Name="btn1" Content="I'm a Button"
                            Padding="10,5" HorizontalAlignment="Center"
                            Click="btn1_Click"/>
    </StackPanel>
</Window>

1040-001
See this post for another example of dependency property inheritance.

#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