What is value coercion?
If you are not familiar with value coercion, it is simply a mechanism by which related (or “interdependent”) dependency properties can be kept in sync and valid.
The quintessential example can be found within the Slider control. A Slider has both Minimum and Maximum properties. Clearly, it would be a problem if the Maximum value were allowed to fall below the Minimum value. Value coercion is used to prevent this invalid state from occuring.
In WPF, the Slider control (or more specifically, the RangeBase control) ensures that these property values stay valid by using a feature of the dependency property metadata called a CoerceValueCallback. Whenever the Maximum property value changes, it is passed through this coercion function. If the Maximum value happens to be less than the Minimum value, the function will coerce it to be equal to the Minimum value so that it is valid.
The coercion routine for the Maximum property looks something like this:
private static object CoerceMaximum(DependencyObject d,
double min = ((RangeBase)d).Minimum;
double max = (double)value;
if (max < min) return min;
Whenever the related Minimum property changes, the control explicitly coerces the Maximum property. This ensures that the Maximum value stays valid with respect to the new Minimum value.
The property changed callback for the Minimum property looks similar to this:
private static void OnMinimumChanged(DependencyObject d,
Notice that changes to the Minimum property also result in coercion of the Slider’s Value property. That’s because the Value property also needs to stay valid. More specifically, it must be kept between the Minimum and Maximum values. That means if either the Minimum or Maximum values change, the Value property must explicitly be coerced. As such, the property changed callback for the Maximum property also coerces the Value property, something like this:
private static void OnMaximumChanged(DependencyObject d,
Clearly, the Value property needs its own coercion routine to keep it between Minimum and Maximum. For completeness, here is what the CoerceValueCallback for the Value property looks like:
private static object CoerceValue(DependencyObject d,
double min = ((RangeBase)d).Minimum;
double max = ((RangeBase)d).Maximum;
double val = (double)value;
if (val < min) return min;
if (val > max) return max;
Base Value vs. Effective Value
It is important to recognize that with value coercion, a dependency property has both a “base” (or “desired”) value and an “effective” (or “coerced”) value. The “base” value is always passed into the CoerceValueCallback and the value returned from that method becomes the new “effective” value.
In the case of the Minimum and Maximum example, if the “base” value of the Maximum property is less than the Minimum value, then the “effective” value of the Maximum property becomes equal to the Minimum value. Otherwise, the “base” and “effective” values are simply equal.
Value Coercion is Not Natively Supported in Silverlight
There’s nothing more frustrating than needing to port something from WPF to Silverlight and realizing that a key feature like value coercion does not yet exist in Silverlight. (For the record, I have no idea whether it will ever be supported natively, but if you use the mechanism provided by my snippets, you should be in great shape if/when we get true native support for value coercion in Silverlight.)
The lack of support for value coercion in Silverlight means you must roll a “do-it-yourself” version of the feature in your classes. The Silverlight Slider control attempts to do this. Unfortunately, this can lead to code that is cumbersome to maintain, especially if you need to use the same mechanism in several different classes.
Furthermore, achieving parity between frameworks can prove to be very difficult. As evidence of this, note that the pseudo-coercion within Silverlight’s native Slider control is just wrong. The Minimum property coerces the Value property upward, but does not coerce it back down correctly (and it’s likewise wrong for the Maximum property).
Value Coercion — There’s a Snippet for That!
To support value coercion in a consistent manner across your Silverlight classes, you can leverage my FrameworkPropertyMetadata class and the related SilverlightCoercionHelper class. Simply add a class file to your project called FrameworkPropertyMetadata.cs and then expand the “dp shc” snippet (Dependency Property — Silverlight Helper Classes) within it.
Whenever you declare a dependency property with a CoerceValueCallback (e.g., the “dp s3″ snippet), you also need to initialize the owner type for value coercion and add a CoerceValue method to the class. To do this, expand the “dp scm” snippet (Dependency Property — Silverlight Coercion Methods) within your class.
To see a demonstration of the snippets in action, check out the video at the end of this post.
If you use my snippets to support value coercion in Silverlight, your code should also compile just fine in WPF. When compiled in WPF, the framework’s native coercion mechanism will be used.
To support this cross-framework compatibility, you will notice that the helper classes and coercion methods in my snippets wrap certain code blocks within the #IF SILVERLIGHT directive.
Video Introduction to Value Coercion
I’ve put together a 14-minute screencast demonstrating all of this. Specifically, this video illustrates how value coercion works within the WPF and Silverlight native Slider controls. It then shows how to use my snippets to create similar interdependent Minimum, Maximum, and Value properties in a custom Silverlight control.
Sr. Programmer Analyst