Archive for the ‘Dependency Properties’ Category

A Few Good Metadata Options for Silverlight

Saturday, May 8th, 2010

I’d like to highlight one more feature that is available in Silverlight if you use my code snippets.

Those who watched the value coercion video might have glimpsed the following enum within the FrameworkPropertyMetadata.cs code file:

[Flags]
public enum FrameworkPropertyMetadataOptions : int
{
    None = 0x0,
    AffectsMeasure = 0x1,
    AffectsArrange = 0x2,
    AffectsParentMeasure = 0x4,
    AffectsParentArrange = 0x8,
}

This enum provides a limited subset of the metadata options that are available in WPF.  Specifically, these are the options used with dependency properties that affect layout.

How do metadata options work?

If you are not familiar with these metadata options, here’s a quick rundown…  In WPF, a dependency property on a framework element can be registered with one or more metadata options.  This is done by combining the desired flags using a bitwise “OR” and then passing that value into the appropriate DependencyProperty.Register() overload.

As an example, the Width property on FrameworkElement is registered with the AffectsMeasure option.  As such, the property engine will take care of invalidating measure on the target element whenever the Width property changes.

The other layout options work pretty much the same way.  The AffectsArrange flag will cause InvalidateArrange() to be called on the target element when the property changes.  The AffectsParentMeasure and AffectsParentArrange flags will cause InvalidateMeasure() and InvalidateArrange() to be called, respectively, on the target element’s parent.

It’s All About Portability

In Silverlight, there is no native mechanism for registering a property to affect measure or arrange.  As such, you typically register a PropertyChangedCallback on the property and then explicitly call InvalidateMeasure() and/or InvalidateArrange() within your callback. 

If you use my snippets, you can follow the exact same approach in Silverlight that you would use in WPF for properties that affect layout. My FrameworkPropertyMetadata class will take care of the necessary layout invalidations.  So a property that affects measure could be declared in Silverlight using my ‘dp s1’ snippet, as follows:

#region VerticalExtent

/// <summary>
/// VerticalExtent Dependency Property
/// </summary>
public static readonly DependencyProperty VerticalExtentProperty =
    DependencyProperty.Register("VerticalExtent", typeof(double), typeof(MyControl),
        new FrameworkPropertyMetadata(double.NaN,
            FrameworkPropertyMetadataOptions.AffectsMeasure));

/// <summary>
/// Gets or sets the VerticalExtent property. This dependency property
/// indicates the vertical extent of the control.
/// </summary>
public double VerticalExtent
{
    get { return (double)GetValue(VerticalExtentProperty); }
    set { SetValue(VerticalExtentProperty, value); }
}

#endregion

What about the other WPF metadata options?

There are several metadata options in WPF that are not included in my Silverlight enum.  These are intentionally excluded because they either cannot be supported outside the native Silverlight framework (e.g., the property engine would need to be modified to support the NotDataBindable option) or they simply don’t make sense in Silverlight (e.g., Silverlight does not currently support direct rendering via an OnRender() override, so I don’t provide an AffectsRender flag).

I hope you find the provided layout metadata options handy!

Cheers!
Dr. WPF

Value Coercion for the Masses

Wednesday, May 5th, 2010

My latest snippets package adds value coercion support to Silverlight dependency properties. The mechanism for coercing values is identical to the CoerceValueCallback approach used in WPF.

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,
    object value)
{
    double min = ((RangeBase)d).Minimum;
    double max = (double)value;
    if (max < min) return min;
    return value;
}

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,
    DependencyPropertyChangedEventArgs e)
{
     ((RangeBase)d).CoerceValue(MaximumProperty);
    ((RangeBase)d).CoerceValue(ValueProperty);
}

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,
    DependencyPropertyChangedEventArgs e)
{
    ((RangeBase)d).CoerceValue(ValueProperty);
}

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,
    object value)
{
    double min = ((RangeBase)d).Minimum;
    double max = ((RangeBase)d).Maximum;
    double val = (double)value;
    if (val < min) return min;
    if (val > max) return max;
    return value;
}

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.

Cross-Framework Compatibility

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.

NOTE: The audio in this video has been “coerced” to protect the guilty! 😉

You can download the source code here for the Silverlight 4 sample coercion application created within the video.

I hope this helps a few folks maintain portable WPF/Silverlight code! 🙂

Cheers!
Dr. WPF

Can I borrow that DP for a little while?

Wednesday, November 14th, 2007

If you’re like me, you sometimes want to prove out a concept in XAML before implementing the “real” solution.  For example, you may want to create a new control which is very similar to an existing control, but lacking a property or two.  Rather than derive the control from a base class and add the new properties, it might be nice to simply re-template the existing control and pretend that it has the necessary properties.

Luckily, WPF makes this fairly easy through its support for attached properties.  It’s very likely that the framework has already defined a couple of properties of the very type you are needing.  If you cannot find an exact match, you can usually find something close enough.

Anyone who follows my posts in the WPF forum knows that I’m a big fan of “borrowing” attached properties from the framework.  This is especially useful in the forum because it allows me to provide a XamlPad-ready solution to demonstrate a concept.  Here are a couple of examples:

Over the years, I have assembled a catalog that includes most of the framework-defined, public attached DPs (yes, I’ve spent far too many hours in reflector and writing code that greps the framework :P), as well as all of the relevant information about those DPs.  For anyone who is similarly inclined to “borrow” from the framework, here is my spreadsheet of attached DPs.

A few words of caution… 

  • You should always know what the owner class does with any property you borrow.
  • You should pay attention to default values and inheritance.  Sometimes you need a bool property with a default value of true… other times you may want a default value of false.  Sometimes you need a property that inherits… other times you explicitly don’t want inheritance.  (Borrowing a property like TextElement.FontSize could really screw up things lower in the tree.)
  • The owner class may sometimes define a PropertyChangedCallback that will interfere with your ability to use the property as you wish.  Always know what the owner class does with the property.
  • The owner class may provide a validation routine for the property that prevents you from entering the value you want to specify.  Again, always know what the owner class does with the property.
  • The property may be registered in a manner that makes it costly perf-wise, such as FixedPage.Bottom which invalidates the parent’s arrange anytime the property changes on an object.  Sometimes you may explicitly want this behavior… other times it will just unnecessarily cause layout passes.  Again, always know what the owner class does with the property.
  • If you use a property in a scenario where the framework itself is trying to use the property (such as TextSearch.TextPath on an ItemsControl), you are liable to find yourself in contention with the framework.

Okay, I’m sure I could go on, but you get the general idea.

The nice thing about this spreadsheet is that it has all of the information you might want to know about the properties, including the following:

  • Defining Class / Owner Type
  • Property Name
  • Type
  • Metadata Type
  • Default Value
  • Owner Handles Changes (the owner registered a PropertyChangedCallback)
  • Coerced (the owner coerces the value)
  • Validation (the owner validates the value)
  • Inherits
  • Metadata Options

It also contains a second worksheet with a pivot table that can be used to easily filter the properties down to exactly those that meet your needs.  And remember, if you can’t find a property that matches the exact type you’re looking for, you can usually just use a DP of type string because there are type converters for most objects that are capable of translating to/from string values.  And there are a few properties of type ‘object’ that can serve as uber-utility DPs.

Let the borrowing begin!
Dr. WPF