ailon's DevBlog: Development related stuff in my life

Writing WPF/Silverlight compatible code. Part 2: Dependency Properties

11/5/2009 7:33:43 PM

Note for future readers: These series discuss WPF and Silverlight versions that are current stable versions at the time of this writing – WPF 3.5 and Silverlight 3.

These are ongoing series of posts on the subject of WPF/Silverlight compatibility. New posts will be added to the Table of Contents post as they are written so bookmark that post or just subscribe to my RSS feed.

DependencyProperty.Register

Dependency properties are one of the core concepts of both WPF and Silverlight. To register a dependency property you use DependencyProperty.Register() method. WPF 3.5 has 3 overloads for that method while Silverlight has only one.

   1: public static DependencyProperty Register(
   2:     string name,
   3:     Type propertyType,
   4:     Type ownerType,
   5:     PropertyMetadata typeMetadata
   6: )

Obviously if you want to create cross-platform code you should use that one method all the time in WPF.

No FrameworkPropertyMetadata

For most WPF framework-level application development purposes, FrameworkPropertyMetadata is the type used for dependency property metadata, rather than the base metadata types PropertyMetadata or UIPropertyMetadata.

from MSDN.

Unfortunately there’s no FrameworkPropertyMetadata class in Silverlight.

One of the most used improvements of FrameworkPropertyMetadata over PropertyMetadata are Boolean settings that control invalidation of various layout aspects of the object such as AffectsMeasure, AffectsArrange, etc. If some of these settings are set to true, corresponding aspect is invalidated when property value changes.

Fortunately it’s not too difficult to mimic this behavior using PropertyMetadata and PropertyChangedCallback. So if you would write something like this for WPF-only application:

   1: public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register(
   2:     "Something", typeof(string), typeof(Window1),
   3:     new FrameworkPropertyMetadata(string.Empty,
   4:         FrameworkPropertyMetadataOptions.AffectsMeasure)
   5:         );

you can rewrite it this way to work in both WPF and Silverlight:

   1: public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register(
   2:     "Something", typeof(string), typeof(Window1),
   3:     new PropertyMetadata(string.Empty,
   4:         new PropertyChangedCallback(Window1.SomethingProperty_Changed))
   5:         );
   6:  
   7: private static void SomethingProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
   8: {
   9:     ((FrameworkElement)d).InvalidateMeasure();
  10: }

To make life easier you can create a helper class with methods for invalidating various aspects and combinations and use methods from that class for properties that do nothing else on change.

No Value Coercion

WPF includes a powerful mechanism for value coercion.

Coerce value callbacks do pass the specific DependencyObject instance for properties, as do PropertyChangedCallback implementations that are invoked by the property system whenever the value of a dependency property changes. Using these two callbacks in combination, you can create a series of properties on elements where changes in one property will force a coercion or reevaluation of another property.

A typical scenario for using a linkage of dependency properties is when you have a user interface driven property where the element holds one property each for the minimum and maximum value, and a third property for the actual or current value. Here, if the maximum was adjusted in such a way that the current value exceeded the new maximum, you would want to coerce the current value to be no greater than the new maximum, and a similar relationship for minimum to current.

from MSDN.

Unfortunately there’s no such mechanism in Silverlight. You’ll have to deal with scenarios like the one above by just using a combination of PropertyChangedCallbacks and regular methods. In some complicated but not critical scenarios you might find it reasonable to use value coercion in WPF and just leave uncoerced values in Silverlight. You can do this by forking your code as described in part 1 of these series.

No OverrideMetadata()

WPF let’s you override property metadata. You can do this using OverrideMetadata method of DependencyProperty. In the code below we override the default width of our control inherited from Control class.

   1: static MyControl()
   2: {
   3:     FrameworkPropertyMetadata newMetadata = new FrameworkPropertyMetadata();
   4:     newMetadata.DefaultValue = 180.0;
   5:     Control.WidthProperty.OverrideMetadata(typeof(MyControl), newMetadata);
   6: }

Again, there’s no OverrideMetadata in Silverlight. When you just want to change the default value of some property you can simply set the value in your default public constructor.

   1: public MyControl()
   2: {
   3:     this.Width = 180;
   4: }

However I'm not sure if or how you can set new PropertyChangedCallback on inherited property in Silverlight. Let me know in the comments if you do.

No read-only dependency properties

One of the really nice features in WPF are read-only dependency properties. These are very useful when you need to have a property reflecting some internally determined state of your object (like IsMouseOver property) and don’t want other developers to be able to change the value from the outside. You can easily do this with regular .NET properties but what if you want a read-only property while still enjoying all the perks of dependency property mechanism?

Unfortunately I have no workaround for Silverlight. Sure, you can create a regular CLR property wrapper for dependency property having only the get accessor, but this wont stop anyone from setting the value using SetValue() method.

So you’ll have to make a choice here (unless you find some really working solution): either forget about read-only dependency properties altogether or use separate code (either using pre-processor directives or partial classes) for WPF and Silverlight. Just remember that you’ll have to use different code for not only your dependency property declarations, but SetValue calls, too (you use SetValue() on DependencyPropertyKey with read-only properties). So things can really get messy with constant #if SILVERLIGHT forking.

kick it on DotNetKicks.com Shout it

Tags: , ,

blog comments powered by Disqus
Copyright © 2003 - 2014 Alan Mendelevich
Powered by BlogEngine.NET 2.5.0.6