On the 17th I began a discussion of Dependency Properties, but as I said then, there is more to say. This central concept to Silverlight Programming has traditionally been taught as an advanced concept.(Can we have traditions in a product that hasn't really been released yet?) And to some degree, with good reason: you can pretty much ignore the Dependency Property System until/unless you're writing custom controls. Here are three reasons not to:
1. They're fascinating
2. They explain a lot about how things work
3. They are critical to creating your own custom controls
Okay, the real reason is #1; they're just interesting.
There are a couple ways to approach Dependency Properties. Most of us approach them practically, to accomplish a specific goal at first ("How do I wire up a DP in this custom control?") and then later, we return to understand the entire system into which they fit. It is the purpose of today's column to begin that second stage: examining the system as a whole.
An Adventure in Spelunking the Dependency Properties System
We need to start by acknowledging a few somewhat surprising facts:
The folks who created WPF and Silverlight made some rather radical (and I would argue technically courageous) decisions:
- they said "the current properties are inadequate for our needs, and we're going to add a new layer on top that will not break the underlying layer but that will give us tremendous additional options."
- Thus, the Dependency Properties system is really a radical extension to the CLR and to that which is available in C++, Java, or even pre-WPF C#/VB.NET.
- Then the folks at Silverlight chose which parts of the system they would use, which they'd adapt and which they'd leave behind based on optimizing for the needs of Silverlight rather than just for pure isomorphic consistency.
Controversial but highly defensible decisions, and ones that will give us early headaches trying to understand all of it, but long term benefits.
The biggest problem, of course, is that figuring out what is really going on can be a bit problematic, especially right now. This particular moment in history is a documentation purgatory.
- The WPF books are excellent but not everything they say applies to Silverlight
- The Beta2 chm file and help files are very good but now partially obsolete
- The change document is helpful but not guaranteed to be complete
- Full documentation of Dependency Properties was never a priority as it was considered somewhat advanced/obscure
So we're in for some fun.
To make this work, I'm going to need to (a) break this up into a few related posts and (b) try to impose some discipline on myself to do the set of DP posts within a relatively short period so I don't forget what I've covered already! I'll tag them all with "Dependency Properties" so you can find them or skip them as you choose. |
Quick Review of Why Dependency Properties were Added
As noted in my posting on the 17th the WPF designers quickly found that standard CLR properties were not responsive enough nor extensible enough to support declarative, animated and databound client-side applications. What was needed was a system that could establish the value of a property, at run time, based on input from a number of sources (e.g., the current value of other properties, rapidly changing animation values, etc.).
A key value of the Dependency Properties system was the ability to build properties that automatically notify any registered interested party each time the value of the property changes. This free, painless and automatic implementation of the observer pattern is tremendously powerful and greatly reduces the burden on the client programmer (in fact, the data-binding system depends on it!).
[We cover the observer patten in detail in Programming .NET 3.5 – for an unauthorized excerpt, click here ]
That alone is worth the price of admission. It means that if you bind numerous controls to dependency properties, you are guaranteed to be notified any time the value of the property changes, without your writing a line of code. Whooah!
Cost – Benefit
I believe (and obviously the designers believed) there is enormous benefit to the Dependency Properties system, but let's not ignore the fact that you do pay a price, if only in learning this system. In this posting I'll cover that price, in the next I'll start to explore some of the benefits.
The price, such as it is….
You need to learn, and grok, a new element in your class – Dependency Properties that interact with an underlying system that didn't usta' exist. More, some of your properties are really just wrappers to these new DP's. What that means is that the backing value for some of your properties is not a member variable or a value in a database or a computation, but a Dependency Property. That takes a little mind-share.
Here's what that looks like in code:
// clr wrapper
public bool Valuable
{
get { return (bool) GetValue( ValuableProperty ); }
set { SetValue( ValuableProperty, value ); }
}
A pretty standard get and set, except that you access your backing variable using GetValue and SetValue to get and set the value of a Dependency property named ValuableProperty (and that is the idiom, the CLR property name + the word Property = the name of the DP, thus Valuable + Property = ValuableProperty.
The declaration of the DP itself is much weirder,
public static readonly DependencyProperty ValuableProperty =
DependencyProperty.Register(
"Valuable",
typeof( bool ),
typeof( MyCustomControl ),
new PropertyMetadata( new PropertyChangedCallback(
MyCustomControl.OnValuablePropertyChanged ) ) );
Let's break this down. The first line declares my object (which is really a reference to a DependencyProperty) as public; it must be static and readonly, and its type is DependencyProperty and its name (identifier) is ValuableProperty.
We set that reference to what we'll get back by calling the static Register method on the DependencyProperty class. Register takes four arguments:
- The name of the dependency property wrapper
- The type of the DP being registered
- The type of the object registering it
- The Callback
The Callback is of type PropertyMetaData. You can imagine a world in which there are various pieces of MetaData for the DependencyProperty. At the moment, however, in Silverlight, there is only one: the callback.
The constructor for the PropertyMetaData takes an object of type PropertyChangedCallback which will be called any time the effective property value of the DP property changes. We pass it a reference to the method to call (which equates to a callback).
The net of all of this is that we present to the world a CLR property (Valuable) which is in fact backed by a DependencyProperty which will call back to the method OnValuablePropertyChanged any time the effective value of the property changes.
The callback method will take two arguments:
- A DependencyObject (the control)
- An object of type DependencyPropertyChangedEventArgs
Typically you'll cast the first argument to be the type of the control that contains the property, and you'll cast the NewValue property of the DependencyPropertyChangedEventArgs object to the DependencyProperty that changed. You can then take whatever action you need to based on the change in the DP's value
[Listing updated 10/1 – 10:30 am]
1: public class MyCustomControl : Control
2: {
3:
4: public static readonly DependencyProperty
5: ValuableProperty = DependencyProperty.Register(
6: "Valuable",
7: typeof( bool ),
8: typeof( MyCustomControl ),
9: new PropertyMetadata( new PropertyChangedCallback( MyCustomControl.OnValuablePropertyChanged ) ) );
10:
11: public bool Valuable
12: {
13: get { return (bool) GetValue( ValuableProperty );}
14: set { setValue( ValuableProperty, value );}
15: }
16:
17: private static void OnValuablePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
18: {
19: MyCustomControl control = d as MyCustomControl;
20: bool b = (bool) e.NewValue;
21: }
22: }
23:
That's pretty much it, except getting your head around why you did all this; and for that we have to begin to explore, in more detail, the benefits of the system. That is, once you have this, what have you gained?
Stay tuned, rather than gloss over it, I'd like to take the time to explore it in depth.
-jesse
hi,
thanks about the article, but still a question remains for me.
WHERE THE ASSIGNED VALUES ARE STORED?
for example can we get list of objects which has a value for specific dependency property.
imagine that i have an attached property called GUIDLable, how can i find the object which i have assigned a local value (GUID by GUIDLablel) for them?
here is an example for clear shut:
Public Shared GUIDLabelProperty As DependencyProperty = DependencyProperty.RegisterAttached("GUIDLabel", GetType(Guid), GetType(AttachedProperties), New FrameworkPropertyMetadata(New Guid("00000000-0000-0000-0000-000000000000"), FrameworkPropertyMetadataOptions.AffectsRender, AddressOf GUIDLabelChanged))
and in Xaml:
where these two GUIDs are stored? and how can i get those two grids?
Thank you very much Jesse. This was the first article on the Silverlight dependency property system that made sense and was well written.
So, the PropertyChangedCallback is kinda sorta like a delegate/event.
You pass in the name of the the method to which a notification will be sent, and voilla.
But let’s say I want more than one control notified. I’m still trying to fully wrap my brain around this. For example, I noticed that the OnValuablePropertyChanged method receives a reference to the DependencyObject which it casts to a control, but the method is located in the same control. I guess that’s just for demonstration? I’m trying to figure out, if you want some method in some control to be notified, why put the method in the same control? Maybe it is similar to being able to raise an event withing a property set. I’m on to part two now.