Dependency Properties
In order to store data in a class you need a place to put it. Traditionally,
classes encapsulate data in the form of member fields that are part of the data
structure of the class. Whenever we create an instance of a class we have a
physical address for the fields of that class. This means ultimately that
classes create memory pressure because of their physical size. Now consider the
situation in which we have millions of objects instantiated, each of which have
properties and fields which all take up memory. It is very possible that a large
roportion of the objects have a default state which will never change throughout
the life of the application. The fields storing such data may be wasting many
megabytes of otherwise useful memory.
A well known pattern which addresses the problems of memory hogging data storage
for object graphs that hold many millions of items is the Flyweight
pattern. This well known design enables an object to hold data only when
it needs to, usually by storing the data in some kind of dictionary or
hashtable. In this pattern the cost of creating the storage space, which may be
a CPU pressure is outweighed by the savings in memory when a large number of
objects gold varying amounts of data.
The reason for the dependency object in WPF is because, unlike previous
graphical systems where it was wise to minimise the number of stored graphical
objects, WPF uses thousands or millions of lightweight objects to provide very
complex graphical representations. The logical tree of objects that we create in
XAML is mirrored and expanded upon in the visual tree so that a simple
representation of a dialog box or window is backed by a plethora of
retained-mode graphical objects. The potential for memory pressure is therefore
very great.
What is declared by an object as a dependency property is in fact nothing more
than an identifier. This static "property" is really a key which associates an
object with a specific storage identifier. For example graphical objects have a
Background property that can be set explicitly or through the use
of templates or styles..
A dependency property has the advantage of being virtual unless actually used.
For example, they have default values which are used unless the value has been
explicitly set or "coerced". The advantage of this is again a question of memory
pressure. The static declaration defines the default value once and is constant.
All objects that use the property without explicitly setting the values use the
defaults. A good example of this is the Transform settings of an object. In XAML
we can declare that an object has a transform but this does nothing more than
provide the virtual or potential storage space for the property. If we don't
change it explicitly then the default value, a unitiy transform that basically
does nothing, is used. The object however could be changed later by, say, an
animation, which will modify the value of the property.
The actual value of a dependency property as seen by the object may be set by
many sources. The origins of these values come from the complex interactions of
the WPF system and have a strict order of precedence. Essentially, there are no
less than eleven ways that a dependency property might be modified. They are
listed here from the lowest to the highest priority.
11. Default value. This is declared in the dependency property and
is used in the case that no other value has been applied.
10. Inherited values. A property might inherit a value from a
parent element. An example of this is the DataContext which is inherited unless
specifically set.
9. Default style, set by a theme. Border and text styles for
buttons or text boxes will use a theme style.
8. Style setters. Styles contained by the, page, application or
animations use setters to modify the value of a dependency property.
7. Template triggers. A property may be set by a template
depending upon a MouseOver or Focused state for example.
6. Style triggers. Similarly, styles can specify triggers that
apply styles according to the value of a property. Maybe a bank balance value
would be shown in red if the account was overdrawn.
5. Implicit Styles. These styles are defined in the resources of
the object or page that uses them and override default styles which are of lower
precedence.
4. TemplatedParent template properties. Dependency properties
which are owned by items in the visual tree and which exist because they have
been created as the result of a template setting are modified using this
principle. You will not declare anything explicitly for this but rather it is
inferred by the action of the template system.
3. Local value. Explicitly set by markup or a SetValue call in
code.
2. Animations. Properties set by animation storyboard setters.
1. Coersion. This is the highest priority exception case which is
to allow you to alter the value of a property despite every other attempt. Some
properties are coerced automatically but in general, you will have to respond to
a callback and set the value in code to override all the other possibilities.
Basically, coersion is a get out clause in which you can say "I don't care what
you think. Do it my way!" To do this you'll use a CoerceValueCallback.
|