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.
2 thoughts on “Dependency Property Factory – Part 2”