Dependency Property Factory – Part 2

This post is a follow-up to an earlier attempt to simplify creating dependency properties.

I didn’t notice a possible problem with the approach of the previous post since it doesn’t cause any problems in the project I use it in. WPF is quite strict when it comes to creating custom dependency properties. There are several ways in which you can do it wrong, and the code in the last post does admittedly, exactly that. “There are established naming conventions regarding dependency properties that you must follow in all but exceptional circumstances.” In my opinion, the ability to misuse an interface, and additionally not even being able to notice it, indicates a design flaw. To quote someone with a lot more experience:

Make interfaces easy to use correctly and hard to use incorrectly. The best way to prevent incorrect use is to make such use impossible. – Scott Meyers

…, and that’s exactly what I’ll try to do.

Convention 1: “The name of the field is always the name of the property, with the suffix Property appended.”

public static readonly DependencyProperty IsSpinningProperty =
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean), typeof(OwnerType));

Convention 2: “Define a CLR “wrapper” property whose name matches the name of the dependency property.”

public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}

Convention 3: “In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue actions, respectively.”

First I want to determine why ignoring these conventions causes problems. I was already aware that XAML bypasses property wrappers, resulting in convention 3, and proposed a solution in the previous post. An exception could be thrown in the factory class to prevent a wrong CLR property wrapper. Apparently XAML bypasses the wrappers by calling GetValue() and SetValue() directly (for performance reasons), using the property name with “Property” suffixed to it as a parameter. E.g. the property with the name “ABC” gets called as GetValue(ABCProperty). This causes convention 1. Convention 2 is a bit vague and not mentioned explicitly why it is necessary. Other problems when not following these conventions are according to msdn:

  • Certain aspects of styles and templates will not work.
  • Most tools and designers must rely on the naming conventions to properly serialize XAML, or to provide designer environment assistance at a per-property level.

Does anyone know concrete examples of problems caused by not following convention 2, or in other words, why is it of any importance?

The factory class mentioned in the previous post doesn’t follow convention 1 at all. Why can I still use the dependency properties through XAML? The setter and binding in the following sample work perfectly. It seems like all XAML really needs is the name of the dependency property as passed with the DependencyProperty.Register() method. This would also make a lot more sense, since the name would be redundant otherwise.

<DependencyPropertyTestControl
    XamlSetTest="100"
    Margin="{Binding RelativeSource={RelativeSource Self},
    Path=XamlSetTest}" />
    public class DependencyPropertyTestControl : Label
    {
        public enum Property
        {
            XamlSetTest
        }
        private static readonly DependencyPropertyFactory<Property> m_properties =
            new DependencyPropertyFactory<Property>( typeof( DependencyPropertyTestControl ) );

        public static readonly Dictionary<Property, DependencyProperty> Properties =
            m_properties.Properties;

        [DependencyPropertyAttribute( Property.XamlSetTest )]
        public string XamlSetTest
        {
            get { return GetValue( Properties[ Property.XamlSetTest ] ) as string; }
            set { SetValue( Properties[ Property.XamlSetTest ], value ); }
        }

        [DependencyPropertyChangedAttribute( Property.XamlSetTest )]
        public static void OnSourceChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            var control = d as DependencyPropertyTestControl;

            if ( control != null )
            {
                control.Content = e.NewValue;
            }
        }
    }

UPDATE:
I posted these questions on the msdn forum to see whether somebody could answer them. The answer seems to confirm my suspicions that the property system only uses the name passed to Register() and not the field identifier. I’m guessing it can access the dictionary of DependencyObject directly and uses the dependency property name as the key to find the property in this dictionary. Still it is not wise to break conventions, so I’ll attempt to adjust the factory so that it enforces them instead. I’m afraid this will make a lot of its code superfluous. Perhaps a helper class is a better approach. Recently I discovered exactly such an implementation, and will check it out.

Make Interfaces Easy to Use Correctly and Hard to Use Incorrectly

Author: Steven Jeuris

I have a PhD in Human-Computer Interaction and am currently working both as a software engineer at iMotions and as a postdoc at the Technical University of Denmark (DTU). This blend of research and development is the type of work which motivates and excites me the most. Currently, I am working on a distributed platform which enables researchers to conduct biometric research 'in the wild' (outside of the lab environment). I have almost 10 years of professional software development experience. Prior to academia, I worked for several years as a professional full-stack software developer at a game development company in Belgium: AIM Productions. I liked the work and colleagues at the company too much to give up entirely for further studies, so I decided to combine the two. In 2009 I started studying for my master in Game and Media Technology at the University of Utrecht in the Netherlands, from which I graduated in 2012.

2 thoughts on “Dependency Property Factory – Part 2”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: