|
by
kirupa | 27 April 2008
In C#, creating
properties in code is very easy. A property called
AuthorName would look
like the following:
- public
string
AuthorName
- {
- get;
- set;
- }
The above code, known
as a CLR property, is good enough for you to use in
most cases that involve setting and accessing a
value. A slight wrinkle in this perfect fabric
occurs when you are using Silverlight and want to do
more.
While you can still
use properties such as what I've shown above,
Silverlight introduces more ways to set and access a
value - ways that a CLR property simply cannot
handle. Some of these ways involve animations, data
binding, styling/templating, and more. They go beyond simple setting
and getting via code.
To deal with this, you
have a new type of a property known as a dependency
property. In this article, I will show you how to
create a dependency property in Silverlight.
The best
way to get introduced to dependency properties is to
look in detail at what one looks like. The above
AuthorName property
defined as a dependency property will look as
follows:
- public
string
AuthorName
- {
- get
{
return
(string)GetValue(AuthorNameProperty);
}
- set
{
SetValue(AuthorNameProperty,
value);
}
- }
-
- public
static
readonly
DependencyProperty
AuthorNameProperty
=
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
Whoa! What was a few
lines earlier now has turned into more than a few
lines of mostly incomprehensible code. Let's try to
make sense of all this.
The basic structure of
a dependency property is made up of two things. First,
you have your CLR wrapper:
- public
string
AuthorName
- {
- get
{
return
(string)GetValue(AuthorNameProperty);
}
- set
{
SetValue(AuthorNameProperty,
value);
}
- }
This wrapper provides
you with easy access to the AuthorName property just
like you would expect with any old CLR property that
you can declare. Internally, it is a bit different
in that the get and
set methods call
GetValue and
SetValue
respectively on a property called
AuthorNameProperty.
The CLR wrapper is the first item that makes up your
dependency property. The second item is what is
known as a dependency property identifier, and in
our code, that is represented by
AuthorNameProperty:
- public
static
readonly
DependencyProperty
AuthorNameProperty
=
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
This line is what
reconciles your above CLR wrapper with the property
system that lives beneath Silverlight. This line can
be divided into two parts as well. The first part is
where you simply declare the identifier:
- public
static
readonly
DependencyProperty
AuthorNameProperty
=
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
Almost always, your
type will be DependencyProperty,
and its modifiers will be public,
static, and readonly. The
second part is where you initialize your dependency
property identifier by registering it:
-
public
static
readonly
DependencyProperty
AuthorNameProperty =
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
Registering your
property is interesting because it really doesn't
make a lot of sense because the behind the scenes
details are hidden from view. The main thing to
remember is that, deep under the hoods, Silverlight
has an advanced property system that keeps track of
the various dependency properties used in an
application. Registering is where the various pieces
of data that make up your dependency property get
stored in that behind-the-alley warehouse that only
Silverlight can access.
Registering your dependency property is handled by
DependencyProperty.Register. The Register method
takes several arguments to help you customize some
of the your dependency property's behavior, so let's
look at those arguments now.
-
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
The first argument you
pass in refers to the name of the DependencyProperty
you wish to register. In general, try to keep the
name the same as that of your dependency property
such as what I've done.
-
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
The second argument
defines the type of your dependency property. In our
case, AuthorName is of type string, so that is the
value I pass in to the Register method.
-
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
The third argument
looks similar to what we wrote earlier. What you are
specifying is the type of the class that owns this
dependency property. This will vary depending on
your scenario, but I have this dependency property
declared inside a user control called
AuthorTextField. Therefore, AuthorTextField is what
I specify here.
-
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(""));
The last argument
determines the metadata that will be carried along
with this dependency property, and that is handled
via the PropertyMetadata class. The term metadata
often indicates something that isn't always useful,
but that may be the wrong impression to get at this
point. As you will see very shortly, the arguments
you pass in here greatly affect how you may end up
using the dependency properties in your application.
Into your
PropertyMetadata constructor, you can specify the
default value for your dependency property, a method
to call when this property's value has changed, or
both. In the next section, let's look at that in
greater detail.
Like I briefly
hinted at in the previous section, you can specify
some additional information about your dependency
property via the PropertyMetadata object.
The first variation you can do is change the
default value your dependency property has. In my original code, I don't have a
default value specified. If you were to examine this
dependency property in Expression Blend, this is
what you would see...a blank field:

[ no default value results in nothing being
displayed ]
Let me specify a
default value instead. My new Register code looks as
follows with a default value specified:
-
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata("Sir
Arthur Conan Doyle"));
When I rebuild my
project and examine this property inside Blend
again, notice what I see this time around:

[ your dependency property's default value is
displayed ]
Instead of being
greeted by a blank value, the default value I
specified is what gets displayed instead. In my
case, the default value is of type string because
that is the type of my dependency property. In
whatever you are doing, make sure that your
dependency property's default value is the same type
as your dependency property. In other words, don't
put your value inside quotation marks to signify a
string just because I have done so!
Another argument you can
specify into your PropertyMetadata object is a
callback method. This is a method that gets called
each time your dependency property's value changes,
and the call to this callback method will look as
follows:
-
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata(new
PropertyChangedCallback(AuthorNameChanged)));
In the above code, the
name of my callback method is called
AuthorNameChanged. What I haven't done yet is
actually create my AuthorNameChanged method, but
that is actually pretty straightforward:
- private
static
void
AuthorNameChanged(DependencyObject
d,
DependencyPropertyChangedEventArgs
e)
- {
- // Code for
dealing with author name changes
- }
There are a few things
that you should note. Because your dependency
property identifier (and by extension your Register
code) is a static variable, an event handler
assigned to it such as AuthorNameChanged must be
static as well.
The second thing to
note are the two arguments passed into your callback
method. The first argument is a reference to your
DependencyObject object, and the second argument is
a reference to your
DependencyPropertyChangedEventArgs.
As long as your
callback method has a static modifier and contains
two arguments of type DependencyObject and
DependencyPropertyChangedEventArgs, you are set!
Each time your dependency property's value is
changed, your callback method will get called and
any code contained in it executed.
The final combination of
arguments you can specify for your PropertyMetadata
is one where you have both a default value as well
as a callback method! This would look as follows:
-
DependencyProperty.Register("AuthorName",
typeof(string),
typeof(AuthorTextField),
new
PropertyMetadata("Sir
Arthur Conan Doyle",
new
PropertyChangedCallback(AuthorNameChanged)));
I specify both my
default value as well as create the new
PropertyChangedCallback object with my callback
method name provided as an argument.
Hopefully this article provided
you with a good dose of information on how to use
dependency properties in Silverlight 2. While
articles like this treat dependency properties as
being special, they are actually very common. For
further reading on a practical use of dependency
properties, I
strongly recommend you read my
10 page article on dependency properties and user
controls. While that article is WPF based, much
of concepts will be similar in Silverlight.
|