in Uncategorized

Avalon: Understanding DependencyObject and DependencyProperty

I’m writing this article in response to a
newsgroup posting
where the reader states:

I am confused by this concept : why it is designed like this? Is
there any document on it’s design purpose and intention? The document in Avalon
CTP is not helpful.

I’m sure a lot of people either are, or will be, wondering the
same things, so I figured I would whip together this article for future
reference.


The
DependencyObject
/
DependencyProperty
design actually exists for several reasons all of
which I’ll ultimately touch on within this article, but let’s start with one of
the main concepts for which this design exists known as “attached properties”.
You’ll encounter the concept of attached properties almost immediately as you
start to use Avalon. Attached properties are dependency properties that other
classes declare that can then be applied to any class you will ever throw into
the Avalon mix. Let’s look at one of the simplest examples of attached
properties which involves positioning an item on a
Canvas
. Canvas has four dependency properties regarding positioning:
TopProperty, BottomProperty, LeftProperty and RightProperty. Now, let’s say we
want to just throw a random UI element onto a Canvas. That UI element’s class
has no prior knowledge of the existence of the Canvas class or how it chooses
to position the elements it contains. Therefore, when I want to put that UI
element “into” a Canvas, I need to decorate it with Canvas specific dependency
properties to get it to lay out correctly.

Right about now you’re probably thinking “Well why not just have
base class for all UI elements with Top, Bottom, Left and Right properties?”.
Well the answer to that is simple if you consider a different layout container
such as the
Grid
. The Grid doesn’t just layout things at a certain static X,Y
coordinate like the Canvas. It uses rows and columns and automagically
positions contained elements based on all the contents of the columns (for X)
and rows (for Y). So, to accomplish this, the Grid offers the following
dependency properties for positioning instead1: ColumnProperty and
RowProperty.

So the next question is: How do these properties get applied to
arbitrary classes? Well first off, the classes obviously need to be
DependencyObject subclasses. From the consumer point of view, DependencyObject
is little more than a glorified Hashtable2. It’s job is to store the
current values of any DependencyProperty that is ever applied to it. Setting a
dependency property is simple:

// Create a button
Button myButton = new Button();

// Button ultimately derives from DependencyObject, so let's set
// some properties so that it can be positioned on a Canvas!
myButton.SetValue(Canvas.TopProperty, new Length(100, UnitType.Pixel));
myButton.SetValue(Canvas.LeftProperty, new Length(100, UnitType.Pixel));

// Add the button to a canvas and it will be positioned at 100,100
myCanvas.Children.Add(myButton);

Actually, you know what? That’s the long winded version because
Microsoft has defined a well-known pattern for manipulating attched properties

that make it a little easier for us. The pattern basically says that the class
that owns the dependency property should provide static accessor methods to
get/set the value on arbitrary DependencyObject instances. So let’s look at
that version:

Button myButton = new Button();

Canvas.SetTop(myButton, new Length(100, UnitType.Pixel));
Canvas.SetLeft(myButton, new Length(100, UnitType.Pixel));

myCanvas.Children.Add(myButton);

Hmmm… wait, that’s a long winded version too!!! Why write any
C# at all? Let’s see how this pans out in XAML!

<Canvas ID="myCanvas" xmlns="http://schemas.microsoft.com/2003/xaml">
  <Button ID="myButton" Canvas.Top="100" Canvas.Left="100"/>
</Canvas>

Remember that pattern I just mentioned? Well XAML relies on it
and translates the Canvas.Top attribute syntax to a Canvas.SetTop call.

Ok, this all seems simple enough now that we’ve been introduced
to it right? So, what’s the big deal? Well, wait… we’ve only scratched the
surface with the introduction of attached properties. Attached properties
aren’t the only place that dependency properties should be used. I
just decided to started with them because they demonstrate one of the key
features that DependencyObject enables, which is the ability to tack
infinite amounts of data onto existing objects without them ever having to know
about it in the first place. So, let’s look at a simpler scenario for
dependency properties now, the
Canvas.HeightProperty
. This could have easily been designed as a plain
vanilla .NET instance property of type Length
on the Canvas class and nobody else will ever apply this property to
anything but a Canvas, so why go through the trouble of making it a dependency
property? Point of fact, it still does appear as an
instance property on the Cavas class, but the reason it’s defined as
a dependency property is simple: to gain generic support from Avalon
in terms of services. The “big deal” once you use dependency
properties is that Avalon can also offer many services to your
DependencyObject subclasses now without having to know about their custom
implementation details (in this case Canvas.Height
). Perhaps the easiest service to demonstrate, which is probably also the most
prominent in Avalon, is
animation
. Any dependency property can be animated. Let’s take that
sample we’ve been working with and animate the attached property a little
bit:

<Canvas ID="myCanvas" xmlns="http://schemas.microsoft.com/2003/xaml">
  <Button ID="myButton" Canvas.Left="100">
    <Canvas.Top>
      <LengthAnimation From="0" To="100" Duration="2"
         RepeatDuration="Indefinite" AutoReverse="True"/>
    </Canvas.Top>
  </Button>
</Canvas>

This will animate the button up and down from 0 to 100 (pixels)
over a two second period. Now admittedly, I probably wouldn’t want to animate
the position of a Button in the real world, but that’s actually the point that
I’m driving at. This could just as easily be a Rectangle or another Canvas or
even my own custom control! The positioning of the element on the Canvas
and animation of it’s properties is done exactly the same way for any
DependencyObject subclass that is ever written from now until eternity.

Ok, ok… I can hear the masses screamning: “Bah! Animation, who
needs it!?”. Fine, fine… what about the rest of these services that are based
on dependency properties then?

Service Description
Default
Values
Provides abillity to set the default value for anyone who uses a
particular dependency property in one location (see
PropertyMetaData.DefaultValue
)
Expressions Provide the ability to base a dependency property on a calculated
value instead of a constant (those familiar with IE’s CSS expression support
already have a head start on this one). Perhaps the coolest part of expressions
is that you can write your own calculation logic quite easily by simply
deriving from
Expression
Databinding Actually implemented as an Expression, provides the ability
to bind data from various sources to our property values.
Inheritance For those familiar with CSS, this is the cascading aspect of
something like a font style being applied to the of your HTML document applying
to all descendant elements without explicit font style declarations of their
own
Styling A
Style
in Avalon is basically3 a declaration of a set of
dependency property values
Property
Invalidation
Avalon strongly encourages the concept of dependency property
caching for performance reasons4, property invalidation is the act
of notifying the caching class that it’s local, cached version is no longer
valid

So, that’s it! Hopefully this article helps you
to understand the necessary level complexity that is introduced by the
DependencyObject/DependencyProperty model. Without
DependencyObject/DependencyProperty, the only way to achevie the same type of
generic behavior in .NET would be through something like reflection
which we all know has serious performance side-effects. The design enables
infinite levels of extensibility and provides powerful services for your
custom classes which allow other developers to use them in ways that you
may never have even imagined.

1 Grid is more
complex than this, but for purposes of this example this is all that is needed
to undersand.
2 In actuality, the design of DependencyObject is more complex with
great consideration for performance.
3 A style can actually get quite a bit more advanced when it defines
a VisualTree
4 For more on cached properties,
check out this section of the SDK

Leave a comment

Comment

  1. I once heard Zhanbo Sun, a tester for the dependency property system, give the best and most concise definition for the dependency system. Its a value computation engine.

    The fact that it stores values is just on of many ways it computes value. Animation, inheritance and databinding being some of the other means.

    Getting an attached property (Canvas.GetLeft(obj)) is like computing the value of a property by looking up a hashtable with obj as the key

    Invalidation takes place when computation ^dependencies^ (are lights coming on now?) change.

    Its also the reason why caching is good. You never really know how expensive the computation (GetValueBase) is going to be so cache the current computed value until it’s invalid.

  2. Hi,

    it seems a runtime-binding technology with its every drawbacks/benefits.
    Focusing on drawbacks, I think the performance is almost the most important viewpoint in UI. As a developer, I think this is a ‘cool feature’, but the avalon designer team missed the point: they make the Avalon developer-friendly, not user-friendly.

  3. Hierogli,

    I believe that you missed the point. Avalon is the presentation part of the system. Making it user-friendly is going to be the task of designers and programmers, because, what has to be user-friendly, if not the user interfaces that are created !

    Actually, to me, Avalon is the perfect concept. You see, i’ve been in the field of computers (technical, programming, etc etc) for a long time, but there is one thing i do not have. I have no sense of art. I cannot design a nice looking app. Of course, its going to be very functional, user aware, etc etc .. but will look plain flat and ugly. Now with Avalon, i can have a friend that has that artistical sense, play with the interface, try new designs and i know that all the coding behind it will not need to be done again and again and again. Talk about the fantasmagorical feeling i can get from that.

    One thing i’d like to know, how do i define a DependencyProperty for a collection, i mean, if i have a collection of MyObject which could be a semi complex object (say, 1 int and 1 float), and i want the property to be coming from a string. A bit like a polyline reads its coordinates. I guess my getter should create object and parse the string itself, right ?

  4. Hi,

    The article on DependencyProperties is very useful. Iam dealing with a scenario where Iam want to use DependencyProperty within a usercontrol, such that triggers can be used to activate storyboards within XAML based on these properties.

    My sample code looks like this:
    public partial class MyControl : UserControl …..

    private static DependencyProperty HasTextProperty = DependencyProperty.Register( “HasText”, typeof(bool), typeof(MyControl), new FrameworkPropertyMetadata(false)); public bool HasText { get { return txtDisplay.Text.Length > 0;}}

    Iam getting a error while trying to implement it…
    {“Error at element ‘Trigger’ in markup file ‘ControlTest;component/mycontrol.xaml’ : ‘HasText’ string is not a valid value for ‘Property’ property of type ‘DependencyProperty’..”}

    Do let me know how can i deal with this error?

    Thanks in advance..

  • Related Content by Tag