Getting the Value of a Dependency Property

By Michael Flanakin @ 1:28 PM :: 6202 Views :: .NET, Development :: Digg it!


WPF and Silverlight have a daunting learning curve. There's no doubt about it. All we can do is take one bite at a time and, eventually, we'll finish the elephant. I've talked about my approach to today's WPF/Silverlight tooling and a good intro to routed events and commands, but there's so much more. I haven't talked about dependency properties, but that's probably the second concept you'll have to grasp (before events/commands) as you ramp up on the wide world of XAML. I'm going to skip over it, for now, because I've found something else worth noting: how the value of dependency properties are determined. Obviously, if you don't understand the concepts behind dependency properties, you'll need to brush up, first.

Traditionally, determining the value of a property is simple: you get it's value (referred to as "local value" in WPF/Silverlight). That's it. A call to person.FirstName would return the value stored in the private person._firstName field. If it hasn't been set, then the default value of that type would be used. Simple. If we need to inject any custom logic here, we typically start by doing so in the accessor. For instance, if we want to ensure the value cannot be empty, we add a check to the setter. Things can (and will) get more complex, tho. For instance, we might have a need to allow others to add in their own validation. This would traditionally be handled by an event handler with a Person.FirstNameChanged event. For better or worse, this is all custom and has a lot of room for "creativity." WPF seeks to standardize this and adds a bit of a framework around dependency properties to do so. Determining the value of a dependency property is accomplished in a five-step process.

  1. Get
  2. Evaluate
  3. Animate
  4. Coerce
  5. Validate

For the most part, these are all pretty simple to understand. The first step is arguably the most complex, in my mind, because getting the base value isn't as simple as the aforementioned person.FirstName example.

1. Get the Base Value

Obviously, you need to know what value you're working with before you can proceed, but with features like templating and property inheritance, what the heck is the value!? In school, you had PEMDAS; in WPF you have... well, something a bit more detailed.

  1. Local value
  2. Style triggers
  3. Template triggers
  4. Style setters
  5. Theme style triggers
  6. Theme style setters
  7. Property value inheritance
  8. Default value

I'm not going to dig into each of these. I simply want to mention a few important aspects to keep in mind. First, "local value" refers to any call to DependencyObject.SetValue() (i.e. Height="123" in XAML or code or Canvas.Left="123" in XAML). The only other thing to concern yourself with, if you're a beginner to dependency properties, is the default value. Default values are not necessarily the same as that of the underlying type. For instance, FrameworkElement.Height has a default value of "NaN" (not a number), despite the fact that it's type, double, has a default of 0. Default types for dependency properties are set when the dependency property is registered.

2. Evaluate

If the value from step one derives from System.Windows.Expression, such as data bindings, WPF converts that to a real value. Pretty self-explanatory.

3. Animate

If the dependency property is currently being animated, the value we've retrieved/evaluated is pretty much ignored in favor of the value set by the animation.

4. Coerce

Next is the injection of custom code via the CoerceValueCallback delegate, if one is registered. Being custom code, you're really left to your imagination on what you can and should do here, but one common scenario is to ensure the value is within expected bounds.

5. Validate

Lastly, we inject one last bit of custom code via the ValidateValueCallback delegate, if one is registered. Validation returns a simple true of false, so there's not much you can do if you made it this far with a bad value. If validation fails, an exception is thrown. For this reason, be sure you take advantage of both coercion and validation, if you have a specific domain you're working in.