Saturday, June 23, 2012

Dependency Property

Dependency properties are the standard form that properties in WPF take. They support change notification, animation, property value inheritance, and data binding, and they support multiple property value providers.

Note from MSDN  
"The purpose of dependency properties is to provide a way to compute the value of a property based on the value of other inputs. These other inputs might include system properties such as themes and user preference, just-in-time property determination mechanisms such as data binding and animations/storyboards, multiple-use templates such as resources and styles, or values known through parent-child relationships with other elements in the element tree. In addition, a dependency property can be implemented to provide self-contained validation, default values, callbacks that monitor changes to other properties, and a system that can coerce property values based on potentially runtime information."

Dependency properties can be implemented only on objects that derive from the DependencyObject class. All WPF elements derive from DependencyObject. If you want to implement dependency properties on a custom class, the class must inherit from DependencyObject. Dependency properties are implemented as normal .NET properties with some extra WPF infrastructure. The dependency property must be registered with the run time in a static constructor, and then it can be set using a standard .NET property wrapper.


To implement and register a dependency property:

1. In a class that inherits from DependencyObject, declare a public, static, read-only variable of the type DependencyProperty. By convention, the name of this variable should be your desired name for the property with the suffix “Property” added to it. Look at the following example:

public static readonly DependencyProperty FlavorProperty;


2. Create a static constructor for the class that registers the dependency property. The DependencyProperty.Register method requires you to provide the name of the .NET property wrapper, the type of the property, the type that owns that property, and an instance of FrameworkPropertyMetadata, which can be used to add optional features to your dependency property. The following example shows a static constructor that registers the dependency property and assumes that your class is named PieClass:

static PieClass()

{

FrameworkPropertyMetadata md = new FrameworkPropertyMetadata();

PieClass.FlavorProperty = DependencyProperty.Register("Flavor",

typeof(string), typeof(PieClass), md);

}



3. Finally, create a .NET property wrapper (also called CLR wrapper) to allow the dependency property to be accessed in code, as shown here:

public string Flavor 

    get { return (string)GetValue(PieClass.FlavorProperty); 
    set { SetValue(PieClass.FlavorProperty, value); }
}

It is important to note that when dependency properties are set by the run time, they are set directly through the GetValue and SetValue methods, not through the .NET property wrapper. Thus, you should not put any additional code in the wrapper because it does not run unless the property is set directly in code. Also, the name of the field is always the name of the property, with the suffix Property appended.

You can provide a static callback method that executes when the property value is changed by specifying a delegate in FrameworkPropertyMetadata, as shown here:

static PieClass() 

    FrameworkPropertyMetadata md = new FrameworkPropertyMetadata(new 
   PropertyChangedCallback(FlavorPropertyChanged)); 
   PieClass.FlavorProperty = DependencyProperty.Register("Flavor", typeof(string), typeof(PieClass), md); 
}

private static void FlavorPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 

// Implementation omitted 
}

Example from my project:


To create a generic button: “Accept button” which will turn Red when any update is made on page using it. 
It has two additional dependency properties apart from WPF button properties - 
IsPageChanged
AcceptContent 

public partial class AcceptButton : Button
   {
public bool IsPageChanged
      {
         get { return (bool)GetValue(IsPageChangedProperty); }
         set { SetValue(IsPageChangedProperty, value); }
      }

      // Using a DependencyProperty as the backing store for IsPageChanged.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty IsPageChangedProperty =
          DependencyProperty.Register("IsPageChanged", typeof(bool), typeof(AcceptButton), new UIPropertyMetadata(false));

public object AcceptContent
      {
         get { return (object)GetValue(AcceptContentProperty); }
         set { SetValue(AcceptContentProperty, value); }
      }

      // Using a DependencyProperty as the backing store for AcceptContent.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty AcceptContentProperty =
          DependencyProperty.Register("AcceptContent", typeof(object), typeof(AcceptButton), new UIPropertyMetadata(null));

     

      public AcceptButton()
      {
         InitializeComponent();
      }
   }


XAML
<AcceptButton x:Name="Accept" Grid.Row="2" AcceptContent="Save" Width="59" Height="27"  Command="{StaticResource SaveCurrentPage}" />

In code behind, 
// Page is no longer changed.
Accept.IsPageChanged = false;


For more example see my posts
 
Note: This is not my original work. Have blogged it here just for my future reference and of course if someone wants to use it.

1 comment: