WPF – Common Dependency Property Exception

Unlike standard .NET properties, implementing a DependencyProperty requires a bit more code, and has the potential to cause nasty errors to creep up at runtime, without any compile time warnings.  Here’s one easy, common mistake, as well as an explanation of why it occurs…

Take a look at the following DependencyProperty implementation:

public static DependencyProperty ValueProperty = 
    DependencyProperty.Register(
        "Value", 
        typeof(double), 
        typeof(Window1), 
        new PropertyMetadata(0));

public double Value
{
    get { return (double) GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}

This code looks perfect, and seems to fit the sample perfectly, but it has a very nasty bug.  If you bind to this property, at runtime, you’ll receive:

XamlParseException

Drilling into the Exception will show you that the entire StackTrace is System XAML errors.  location is your Window or UserControl’s InitializeComponent() call – not too helpful.

The InnerException isn’t much better: “Exception has been thrown by the target of an invocation.”  It, too, has a shorter StackTrace full of “CreateInstanceFromType” errors.  Again, not too helpful.

However, the InnerException of that InnerException is more promising: “The type initializer for ‘WpfCSharpApplication.Window1’ threw an exception.”  This exception at least tells us its something that’s happening when we initialize our Window, providing a scope.  (In this case, we already knew this, but still more useful than before.)

This exception, again, has another level of InnerException, and here, finally, we get some meaningful information: “Default value type does not match type of property ‘Value’.”  The StackTrace of this exception is finally more helpful, as well:

   at System.Windows.DependencyProperty.ValidateDefaultValueCommon(Object defaultValue, Type propertyType, String propertyName, ValidateValueCallback validateValueCallback, Boolean checkThreadAffinity)
   at System.Windows.DependencyProperty.ValidateMetadataDefaultValue(PropertyMetadata defaultMetadata, Type propertyType, String propertyName, ValidateValueCallback validateValueCallback)
   at System.Windows.DependencyProperty.RegisterCommon(String name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
   at System.Windows.DependencyProperty.Register(String name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
   at System.Windows.DependencyProperty.Register(String name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata)
   at WpfCSharpApplication.Window1..cctor() in C:\Users\Reed\Documents\Visual Studio 2008\Projects\TestProjects\WpfCSharpApplication\Window1.xaml.cs:line 12

Looking at this, we can finally narrow down (after drilling through four levels of exceptions!) that our issue is something to do with the DependencyProperty defined on line 12 – in our case, ValueProperty.  Also, we know that the default value type does not match.

Look again at the ValueProperty declaration:

public static DependencyProperty ValueProperty = 
    DependencyProperty.Register(
        "Value", 
        typeof(double), 
        typeof(Window1), 
        new PropertyMetadata(0));

We’re declaring and defining this to be of type System.Double [typeof(double) in the second parameter].  We’re also giving it a default value of 0.  Intuitively, this seems fine, but there is a very serious problem here.

The constructor for PropertyMetadata we’re using to supply a default value has this declaration:

public PropertyMetadata(Object defaultValue)

In order to handle any type of property, the dependency property system in WPF uses System.Object to pass the default value.  When we pass 0 to the defaultValue parameter, the compiler does the equivalent of the following:

int tempValue = 0;
object tempObject = tempValue; // Box int into System.Object
PropertyMetadata tempMeta = new PropertyMetadata(tempObject);
// Pass tempMeta into DependencyProperty initializer

We’re creating an integer (since a “0” in C# will be handled as an integer), boxing it into an object, and passing that to our PropertyMetadata constructor.  Later, when we bind to this dependency property, the property system sees that the type is a double, and attempts to do something along the lines of:

object tempObject = GetValue(ValueProperty); // Get the object
double value = (double)tempObject;

This, unfortunately, fails, throwing an InvalidCastException, because tempObject is a boxed integer, not a boxed double, and you cannot unbox a value type and cast it to a different value type in a single operation.

This is described in section 4.3.2 of the C# Language Specification:

For an unboxing conversion to a given non-nullable-value-type to succeed at run-time, 
the value of the source operand must be a reference to a boxed value of that 
non-nullable-value-type. If the source operand is null, a System.NullReferenceException 
is thrown. If the source operand is a reference to an incompatible object, a 
System.InvalidCastException is thrown.

This is demonstrated in this short console program:

namespace CSharpConsoleProject
{
     class Program
    {
        static void Main(string[] args)
        {
            int tempInteger = 3;
            System.Object tempObject = tempInteger;
            
            // This is, of course, fine, as expected, 
            // because you can implicitly convert from int to double
            double tempDoubleDirect = (double) tempInteger;

            // This, is also allowed, since it's unboxing into an
            // System.Int32, then casting to double
            double tempDoubleTwoCasts = (double) (int) tempObject;
 
            // This is not allowed.  You cannot unbox and cast in one operation
            double tempDoubleCast = (double) tempObject;
        } 
    }
}

The program above will throw on the last line.  Eric Lippert blogged about this in his post: Representation and Identity, and gave a very detailed explanation as to why this is not allowed.

If we apply this to our dependency property declaration, the answer is simple – we just need to force the compiler to treat the default value as the appropriate type (double), and everything will work fine:

public static DependencyProperty ValueProperty = 
    DependencyProperty.Register(
        "Value", 
        typeof(double), 
        typeof(Window1), 
        new PropertyMetadata(0D));

This works because we’ve added the suffix “D” to our integer value, which forces it to treat it as a double.  We could have put in “0.0” instead of “0D”, since literal values with a decimal separator are treated as doubles by default, but I recommend explicitly using the appropriate literal suffix instead.

The issue arises with floating point types other than double.  If you use a float or decimal, you must specify the suffix “f” or “m”.

Integer types are trickier, since the type chosen depends on the value (it will choose int, uint, long, then ulong, depending on the value).  My recommendation is to always use the integer suffix when using unsigned types, as well as when using short or long types.  This prevents accidental issues which could arise by changing the default value in the future.

About Reed
Reed Copsey, Jr. - http://www.reedcopsey.com - http://twitter.com/ReedCopsey

Comments

9 Responses to “WPF – Common Dependency Property Exception”
  1. You’re right, what do you need to do is:

    public static DependencyProperty ValueProperty =
    DependencyProperty.Register(
    “Value”,
    typeof(double),
    typeof(Window1),
    new PropertyMetadata(0d));

    Cheers. Luis.

  2. Reed says:

    Hi Luis! Thanks for the comment – Note that this was my suggested solution at the end (although putting in new PropertyMetadata(0.0) works as well). I wanted to point out exactly WHY this occurs, so people are aware of it being a problem, since I recently had a not-so-fun experience of trying to debug this deep inside of a DependencyObject where it was not obvious. (I was doing this on a behavior, which makes the debugging info even less readable.)

  3. Steve says:

    Now THAT’s a good blog article !
    I always appreciate it when someone really knows what they’re talking about.

  4. Steve says:

    Here’s a suggested topic: FrameworkPropertyMetadata and setting the default binding mode (as well as a host of other settings).

  5. Piotr says:

    Thx. It helps a lot.
    I used incorrect third parameter with similar result.

  6. Chris27 says:

    Brilliant article! Thank you so much for this – I’ve just been trying to debug exactly the same scenario without any real clue as to what was happening. Turns out my custom object Type that I’m setting via the DependencyProperty doesn’t ‘unbox’ nicely from a UIPropertyMetadata object – I’ve just removed that parameter from DependencyProperty.Register() and everything works!

    Cheers, Chris

  7. a says:

    Thanks a lot,
    It helped me.

  8. This posting, Window Covers “WPF – Common Dependency Property Exception : Reed Copsey, Jr.
    ” ended up being remarkable. I am creating out a
    reproduce to demonstrate to my associates.
    Many thanks,Joanna

Trackbacks

Check out what others are saying about this post...
  1. [...] This post was mentioned on Twitter by Reed Copsey, Jr., Reed Copsey, Jr.. Reed Copsey, Jr. said: Published a new blog post: WPF – Common Dependency Property Exception http://cli.gs/Unt6S [...]

    [WORDPRESS HASHCASH] The comment’s server IP (208.74.66.43) doesn’t match the comment’s URL host IP (74.112.128.10) and so is spam.



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!