Recently I tried to make my first attached dependency property and it struck me as odd that I could find a lot of articles on dependency properties – but it still took me quite some time to piece together the how and the why. And basically they all parrot the same few samples. So I decided to try my own take on the subject.
So what is an attached dependency property, then?
It helped me a lot to think of an attached dependency property as being the property equivalent of an extension method. You basically attach a new property to an existing object. Without actually modifying the object definition.
What is it good for?
About the same as an extension method: you can add extra properties to existing objects, without having to go trough the hoopla of inheritance. Which is especially handy when inheritance is not possible, like in sealed objects. More importantly, these properties can play along in the WPF en Silverlight data binding. The most important application, as far as I am concerned, is that you can make something bindable that is not bindable out of the box. If some value(s) a GUI component can only be set by a method, you cannot use it from MVVM, for example. By making an attached dependency property that passes on the new value to said method you work around that. See below for how you make such a callback.
In fact, attached dependency properties is part of the magic of that makes Laurent Bugnion’s MVVM Light toolkit go, at least where the ButtonBaseExtensions class is concerned, and reading the code in this class actually (and finally) made the penny drop for me.
So how do you make an attached dependency property?
You make a static class, in which the property is declared and 'hosted', as I call it. The general form is like this:
public static class DependencyPropertyHoster { public static readonly DependencyProperty MyPropertyNameProperty = DependencyProperty.RegisterAttached("MyPropertyName", typeof(TargetPropertyType), typeof(DependencyPropertyHoster), new PropertyMetadata(CallBackWhenPropertyIsChanged)); // Called when Property is retrieved public static TargetPropertyType GetMyPropertyName(DependencyObject obj) { return obj.GetValue(MyPropertyNameProperty) as TargetPropertyType; } // Called when Property is set public static void SetMyPropertyName( DependencyObject obj, TargetPropertyType value) { obj.SetValue(MyPropertyNameProperty, value); } // Called when property is changed private static void CallBackWhenPropertyIsChanged( object sender, DependencyPropertyChangedEventArgs args) { var attachedObject = sender as ObjectTypeToWhichYouWantToAttach; if (attachedObject != null ) { // do whatever is necessary, for example // attachedObject.CallSomeMethod( // args.NewValue as TargetPropertyType); } } }This is roughly the equivalent of
public partial class ObjectTypeToWhichYouWantToAttach { TargetPropertyType _myPropertyName; TargetPropertyType MyPropertyName { get { return _myPropertyName; } set { if( _myPropertyName != value ) { //CallBackWhenPropertyIsChanged... } } } }
The whole thing works by naming convention. So if you call your property "MyPropertyName", then:
- Your DependecyProperty needs to be called MyPropertyNameProperty
- Your Set method needs to be called SetMyPropertyName
- Your Get method needs to be called GetMyPropertyName
- The name of your property (as a string, yes)
- Property value type (here “TargetPropertyType”, but of course that can be anything)
- Type of the hosting class (i.e. DependencyPropertyHoster in this case)
- Callbackmethod to be called when the property is changed.
How do you use it from XAML?
First, you need to declare its namespace and possibly its assembly. Suppose my class DependencyPropertyHoster was in namespace LocalJoost.Binders, and in a separate assembly called LocalJoost, I would need to declare it as
xmlns:MyPrefix="clr-namespace:LocalJoost.Binders;assembly=LocalJoost"and if you want to use it in binding, you use
MyPrefix:DependencyPropertyHoster.MyPropertyName="{Binding whatever}"
I am currently making a small mapping application using a MultiScaleImage for showing Bing Maps that heavily depends on this trickery. Stay tuned, an example of this will probably follow shortly.
But wait, where is the actual data stored if not in the object itself?
To be honest, I don’t really know. An explanation is provided in the first comment by Dennis Vroegop (thanky you!). But to be even more honest, for now I don’t really have to know. If it works, it works. That's the real beauty of abstraction :-)
Update: for the real lazybones, a code snippet
Unzip this file in your code snippet directory. You will find this in Libraries\Documents\Visual Studio 2010\Code Snippets\Visual C#\My Code Snippets. From now on, if you type “adp” followed by a tab it will automatically insert an attached dependency property for you