LA.NET [EN]

Jan 20

Dependency properties and XAML gotchas

Posted in Silverlight      Comments Off on Dependency properties and XAML gotchas

As I’ve said before, dependency properties introduce several advantages over the traditional CLR properties. However, there might some gotchas associated with their use from XAML. As we’ve seen, dependency properties are defined through static fields and exposed through .NET properties for easy consume from C#/VB code (as we’ve seen, getting and setting the value of the property is achieved through the GetValue/SetValue methods inherited from the DependencyObject class). If you’re coming to Silverlight from WPF, you already know that getters and setters of the property wrappers are “bypassed” at runtime when you set the properties from XAML and that’s probably what’s going to happen in Silverlight too. Is it? Are you sure? The docs say that is the correct behavior, but I’m not seeing that in my Silverlight 4.0 tests…

Take a look at the following (dumb) custom control:

public class MyTextBox:TextBox {
 public static readonly DependencyProperty DurationProperty;
 static MyTextBox() {
        DurationProperty = DependencyProperty.Register(
                  "Duration",
                  typeof(Int32),
                  typeof(MyTextBox),
                  new PropertyMetadata( 0,
                  (sender, e) =>{} )
        );
 }
 public Int32 Duration {
    get { return (Int32)GetValue(DurationProperty); }
    set {
          if (value <= 0) {
            throw new ArgumentOutOfRangeException();
          }
          SetValue( DurationProperty,value ); 
} } }

Now,check the following XAML:

<my:MyTextBox Duration="-1" /> 

If you’re expecting to see an exception during loading, then you’re correct. If you’re thinking that the code will run without any problems, then you’re wrong. This is another interesting gotcha that might bite you if you’re coming to Silverlight from WPF (btw, the docs *do* say that the behavior is the same as the one we get in WPF, but the truth is that I’ve run the previous code and the setter is always hit when setting the value from XAML – is this a changed when compared with SL 3?). 

So, how can you build a control which works in both platforms with minimum work? For instance, in the previous example,  I wanted to make sure that you can only pass positive values to the Duration property. The correct way of doing that type of check is by relying on the metadata that is passed during the property registration. Take a look at the revised code:

static MyTextBox() {
    DurationProperty = DependencyProperty.Register(
                "Duration",
                typeof(Int32),
                typeof(MyTextBox),
                new PropertyMetadata( 0,
                (sender, e) =>
                {
                  if ((Int32)e.NewValue <= 0) {
                   throw new ArgumentOutOfRangeException();
                  }
                })
    );
}
public Int32 Duration {
    get { return (Int32)GetValue(DurationProperty); }
    set { SetValue( DurationProperty, value ); }
}

As you can see, we’re passing a lambda to the property changed callback method. Whenever you try to set a new value, this method ends up being called. The NewValue property gives you access to the new value that is being passed to the dependency property and that’s why we’re using it to see if we’re getting a positive number.

Besides getting the new value, you can also get a reference to the current existing value (OldValue property) and a reference to the dependency property that is being changed (Property property). And I guess that is all for now. Stay tuned for more on Silverlight.