Recently, I was busy building a custom WinRT behavior, and I ran into what I thought was an unusual problem: I created a dependency property (IList<T>), but somehow the PropertyChangedCallback would never fire. Sounds familiar?
This is the code:
public static DependencyProperty ValidationErrorsProperty = DependencyProperty.Register("ValidationErrors", typeof(IList<ValidationError>), typeof(ValidationErrorBehavior), new PropertyMetadata(null, OnValidationErrorsChanged )); public IList<ValidationError> ValidationErrors { get { return (IList<ValidationError>)base.GetValue(ValidationErrorsProperty); } set { base.SetValue(ValidationErrorsProperty, value); } }
I was a bit flabbergasted (always wanted to use that word ;-)), really. I’d written stuff like this in WPF & Silverlight before, without any problems. So I enabled native code debugging to check out what was going wrong. The problem was related to a converter error: “failed to convert List<ValidationError> to IList<ValidationError>”. So it wasn’t just the PropertyChangedCallback that wasn’t firing – it’s a conversion problem (by the way, this is why native debugging is a *really* great option when you run into problems like this).
At first glance, that might look like a bit of a weird problem: why wouldn’t the built-in converter be able to convert a List<T> to IList<T>, while List<T> implements IList<T>? Well, it’s a problem of implicit conversion, which is what’s used by the built-in converter: List<T> implements IList<T>, so it can implicitly convert that way, but logically, not the other way around.
So, how do you solve something like this? There’s two different solutions, depending on the scenario you’re trying to achieve. The first one is removing the need to convert by working with List<T> instead of IList<T>. We’re now working with an implementation instead of with an interface – so there’s no need to implicitly convert anymore. I’m not a big fan of this, though – this would require anyone working with the behavior to provide a List<T>, instead of any collection type that implements IList<T>. In other words: it imposes constraints on the consumer that shouldn’t be there. That scenario results in this code:
public static DependencyProperty ValidationErrorsProperty = DependencyProperty.Register("ValidationErrors", typeof(List<ValidationError>), typeof(ValidationErrorBehavior), new PropertyMetadata(null, OnValidationErrorsChanged )); public List<ValidationError> ValidationErrors { get { return (List<ValidationError>)base.GetValue(ValidationErrorsProperty); } set { base.SetValue(ValidationErrorsProperty, value); } }
Another solution is registering the DependencyProperty as typeof(object) instead of typeof(IList<T>), BUT at the same time constraining what can be provided by ensuring the bindable property is of type IList<T>, so the consumer can’t just provide any object. And this fits my requirements: failed implicit conversion is avoided, and the consumer can provide anything that implements IList<T>. This results in this code:
public static DependencyProperty ValidationErrorsProperty = DependencyProperty.Register("ValidationErrors", typeof(object), typeof(ValidationErrorBehavior), new PropertyMetadata(null, OnValidationErrorsChanged )); public IList<ValidationError> ValidationErrors { get { return (IList<ValidationError>)base.GetValue(ValidationErrorsProperty); } set { base.SetValue(ValidationErrorsProperty, value); } }
Hope this helps you out if you encounter something like this. 🙂
By the way, another tip, related to this: don’t forget to initialize the collection property in the constructor of your behavior – if you don’t do that, your collection dependency property will be shared across different behavior instances; pretty sure you don’t want that to happen 🙂
Happy coding!