Archive

Archive for the ‘Dependency Property’ Category

Shared Dependency Property

February 10, 2009 Leave a comment

Dependency property defined in one class can be shared between different classes e.g. TextBlock.FontFamily and Control.FontFamily share the dependency property defined in the TextElement class.

Dependency property can be shared with other classes by calling a ‘AddOwner’ method on dependency property  which is being shared as shown below ;

TextBlock.FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));

AddOwner method also has one overload which accepts the FrameWorkPropertyMetadata object. So using this metadeta object property metadata can be specified.

However, sharing dependency property can be misleading sometimes. Following example describes that scenario :

Class T1 derives from Textbox having dependency property ‘IntTestProperty"’ defined in it. Class T2 shares the “IntTestProperty” implementation with the class T1.

In xaml code, I have defined style where I’m setting the dependency property “IntTest” values for both T1 & T2 types.

code behind file :

   // This class actually defines the Dependency property

     public class T1 : TextBlock
    {

       // Definition of Dependency property
        public static readonly DependencyProperty IntTestProperty = DependencyProperty.Register("IntTest", typeof(int), typeof(T1));

        public int IntTest
        {
            get { return (int)GetValue(IntTestProperty); }
            set { SetValue(IntTestProperty, value); }
        }
    }

    // definition of class which uses the dependency property defined above
    public class T2 : TextBlock
    {
        public static readonly DependencyProperty IntTestProperty = T1.IntTestProperty.AddOwner(typeof(T2));
        public int IntTest
        {
            get { return (int)GetValue(IntTestProperty); }
            set { SetValue(IntTestProperty, value);}
        }

}

Xaml code :

// style definition to apply on object

<Style x:Key="TestStyle">
    <Setter Property="dc:T1.IntTest" Value="5"/>
    <Setter Property="dc:T2.IntTest" Value="10"/>
</Style>

     <Canvas Left="300" Style="{StaticResource BigFontStyle}">
            <dc:T1 x:Name="t1"  Style="{StaticResource TestStyle}"/>
            <dc:T2 x:Name="t2"  Style="{StaticResource TestStyle}"/>
        </Canvas>

 

After initialization, if we read the value of the “IntTest” property for object t1 and t2 then value of both will be 10. As per the style definition the value of t1 object’s “IntTest” property should 5 and t2 object’s “IntTest” property should be 10. But because t1 and t2 share the same dependency property infrastructure, code sets the value of dependency property twice and when value is read for the property it returns the latest value set on it. That is why value is read as 10 after initialization with above code.

Advertisements

Dependency Property

December 26, 2008 3 comments

Dependency property is new property mechanism introduced by WPF. It is very much different from the normal .net properties but it is very important as many of the WPF’s advance features like styling, triggers, databindings, animations etc. can only work with dependency properties only. Dependency property derives it’s name from the fact that it depends on multiper providers to determine it’s value at any point of time. Dependency property is new property mechanism introduced by WPF.

Implementation :

Following code shows the implementation of dependency property “IsCancelProperty” inside the button control.

——————————————————————————————————————————————————————————————

public class Button : ButtonBase
{
// The dependency property
public static readonly DependencyProperty IsCancelProperty;
static Button()
{
// Register the property
Button.IsCancelProperty = DependencyProperty.Register(“IsCancel”,
typeof(bool), typeof(Button),
new FrameworkPropertyMetadata(false,
new PropertyChangedCallback(OnIsDefaultChanged)));

}
// A .NET property wrapper (not must)
public bool IsCancel

{
get { return (bool)GetValue(Button.IsCancelProperty ); }
set { SetValue(Button.IsCancelProperty , value); }
}
// A property changed callback (not must)
private static void OnIsCancelChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e) { … }

}

————————————————————————————————————————————————————————–

By convention, all the dependency properties are public, static and have “property” as suffix. Dependency properties are created by calling a static “DependecnyProperty.Register” method. “DependencyProperty.Register” method needs information like property name, property type, type of the class on which the property has been defined. Optionally, metadata and callback information can also be passed into the Register method.

In above code, traditional .net property(ISCancel) implements its accessor by calling a GetValue and SetValue methods(these methods inherits from System.Windows. DependencyObject). Calls like Button.GetValue(IsCancelProperty) / Button.SetValue(IsCancelProperty,true) is valid but implementing .net style property wrapper “IsCancel” makes reading and writing of the fields easier and user friendly.

Point to Remember

Although XAML compiler makes use of the .net property wrapper at compile time, at run time WPF calls the GetValue/SetValue methods directly. So to maintain the parity between the compile time and runtime operation, it is important that the property wrapper implementation does not include anything accept the GetValue/SetValue call.

Another important point is “IsCancelProperty” is static field and not the instance field, hence the dependency property implementation saves the per instance memory compared to typical .net property e.g. if button has got say 100 .net properties than each instance of button will result in creation of 100 fields for property information. This results in use of extensive memory as the no. of objects increases. But with dependency property it is not the case.

Motivation :

Now what is the motivation behind putting up this complex property mechanism in place> Following are the reasons :

1. Change Notifications

2. Property Value Inheritance

3. Support for Multiple providers

Change Notification

Whenever the value of the dependency property changes, WPF can automatically trigger number of actions depending upon the property’s metadata. One of the most important feature enabled by this built in change notification is property triggers, which enables to perform custom action when property value changes without writing any procedural code.

Property Value Inheritance

Property value inheritance means allowing the properties of the parents to flow down to the children in a tree e.g. if FontSize(which is dependency property) value is set on to the window class, the same FontSize value will also be applied to all the children of the Window for which the FontSize value is not explicitly set. If for particular children, FontSize is set explicitly the parent settings will not be applied.

Point to Remember

Not all dependency properties participate in Property Value Inheritance. Dependency properties can opt in to inheritance by passing the FrameworkPropertyMetadataOptions.Inherits to DependencyProperty.Register.

Support for Multiple providers

Multiple sources try to set the value of the dependency property however there is a defined mechanism based on which the final value is arrived at for dependency property. It is basically a five step process after going through which the value of the dependency property is finalized.

1. Determine the Base Value

Following eight providers can set the base value for property in chronological order :

  • Local Value
  • Style triggers
  • Template Triggers
  • Style setter
  • Theme style triggers
  • Theme style setters
  • Property value inheritance
  • Default Value

2. Evaluate

If the value from step 1 is expression,  then WPF performs a special evaluation step to convert the expression into concrete result.

3. Apply Animation

If animations are running then they can alter/overwrite the value of the property derived from above two steps. Animation has higher precedence than the local value so animation can overwrite the local value.

4. Coerce

Property value derived from above step is passed to the CoerceValueCallback delegate, if registered with the property. This callback method provides the new value based on the custom logic set in callback. Property value derived from above step is an input to callback delegate.

5. Validate

Finally, coerced value is passed to ValidateValueCallback delegate, if registered with the property. This callback may have some validation checks. Callback returns true if value is valid and sets the property value. However, if Callback returns false, it raises in exception and entire process gets cancelled and value is not applied to property.

P.S. :-

The above explanation of the topic may be bit theoretical but in coming days I will try to come out with the detail explanation of each point with illustrations.