Here’s a quick list of gotchas I’ve learnt the hard way or seen over the time while working with Dependency Properties in Silverlight and WPF (most of this applies to Attached Properties as well).
Here’s a typical dependency property defined on a class inheriting from DependencyObject:
public int SomeValue
{
get { return (int)GetValue(SomeValueProperty); }
set { SetValue(SomeValueProperty, value); }
}
public static readonly DependencyProperty SomeValueProperty =
DependencyProperty.Register("SomeValue", typeof(int), typeof(ownerclass), null);
private static void OnSomeValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ownerclass obj = (ownerclass)d;
int newValue = (int)e.NewValue;
int oldValue = (int)e.OldValue;
}
Naming
The naming is very important. Note ‘SomeValue’ is not only the name of the property, but the static DependencyProperty field is prefixed with the word ‘Property’, and the string in the first parameter also must match this name.
Type and default values.
Below we instead create a dependency property of type double and define a default value in PropertyMetadata to 0. See if you can spot the bug:
public static readonly DependencyProperty SomeValueProperty =
DependencyProperty.Register("SomeValue", typeof(double), typeof(ownerclass), new PropertyMetadata(0));
The problem is very subtle, and the error you will get at runtime is not always obvious. The reason is that PropertyMetadata takes an object as parameter, and there’s no way for the debugger to know that you meant to put a double there, and it will wrongly assume Int32. At runtime you will get an exception because of this. Instead make sure you explicitly declare the default value as a double:
public static readonly DependencyProperty SomeValueProperty =
DependencyProperty.Register("SomeValue", typeof(double), typeof(ownerclass), new PropertyMetadata(0d));
Default values are static – Beware of object references!
Don’t ever ever EVER declare a default value in a DependencyProperty that is not a value type. Since this field is static, the default value will be shared among every instance that’s using the default value.
If I were to access the property and modify its sub properties, anything using this instance would be affected.
Example: Brush property on a control. I have multiple instances of the control, but changing a subproperty on the property will affect all the controls:
public static readonly DependencyProperty MyBrushProperty =
DependencyProperty.Register("MyBrush", typeof(SolidColorBrush),
typeof(ownerclass), new PropertyMetadata (new SolidColorBrush(Colors.Red)));
. . .
myControl.MyBrush.Color = Colors.Blue;
Instead set the default value to null. Then either set the property in the default style template (if it’s declared on a control), or set it in the constructor of your class.
Code in property setter
Often you are used to execute code in the setter of your property when a value changes like so:
public int SomeValue
{
get { return (int)GetValue(SomeValueProperty); }
set
{
if(value != SomeValue)
{
SetValue(SomeValueProperty, value);
UpdateValues(value);
}
}
}
However, if you’ve done this, you might have seen that sometimes your setter is not called, and the UpdateValues() method is never executed. The reason is that if you are binding to this property instead of just calling the setter directly, this setter will never be hit! Instead Silverlight/WPF will call SetValue(…) directly and “circumvent” your setter (this is also why the naming mentioned above is important so it can find the property).
Instead the correct way is to declare a method to call when the value changes. This is done in the PropertyMetadata instance: Also note that you do not need to check whether the value actually has changed. The changed handler will only be called if the value is actually different.
public static readonly DependencyProperty SomeValueProperty =
DependencyProperty.Register("SomeValue", typeof(int), typeof(ownerclass), new PropertyMetadata(0, OnSomeValuePropertyChanged));
private static void OnSomeValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ownerclass obj = (ownerclass)d;
int newValue = (int)e.NewValue;
obj.UpdateValues(newValue);
}