Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Class to Support Attaching to Dependency Property

0.00/5 (No votes)
16 Jun 2011 1  
Provides a way to connect to an external DependencyProperty

Introduction

Sometimes, it is convenient to be able to observe a DependencyProperty change when the observer is not the owner of the DependencyProperty and the owner has not set up any way to set up notification. Microsoft has set up a way to attach to a DependencyProperty using the static method DependencyProperty.RegisterAttached, however, it is best to encapsulate the method in a class.

Background

Generally, there is no need to attach to a DependencyProperty change event from an external class. If there is a need, an Observer pattern can often be implemented by providing an event that can be subscribed to. However, there may be the need to observe a property in a class that cannot or should not be modified to provide such an event. One of the nice things about Dependency Properties is that it is possible to observe a DependencyProperty without needing to customize class containing the DependencyProperty that needs to be observed. I ran into a case where I was creating a Behavior to recognize hover condition, and needed to know when buttons contained in the control were in the Pressed state. It is fairly easy to attach to a DependencyProperty using the DependencyProperty’s RegisterAttached method. However, because of the transitory nature of the behavior, I needed to include the capability to clear the dependency property to prevent potential memory leaks. To be able to clear a DependencyProperty, a pointer to the DependencyProperty created by the DependencyProperty.RegisterAttached method must be maintained even though the only purpose for executing the DependencyProperty.RegisterAttached method is to attach a PropertyChangedCallback to the existing DependencyProperty. However to clear the DependencyProperty (FrameworkElement.ClearValue(DependencyProperty)), you have to have a reference to the DependencyProperty. The value should be cleared to ensure that there are no memory leaks if the class is referenced by a static field.

The following is a class for creating a binding using a local instance of the class. This is the best option if there will be differences in the controls that contain the DependencyProperty for which a binding is desired. A Dispose method is included so as to ensure that there are no memory leaks when the AttachedDependencyProperty is dereferenced.

public class AttachedDependencyProperty<T> : IDisposable
{
    DependencyProperty _dependencyProperty;
    FrameworkElement _element;
 
    public AttachedDependencyProperty(string propertyName, FrameworkElement element, 
               PropertyChangedCallback callback)
    {
        _element = element;
 
        //Bind to a dependency property
        _dependencyProperty = DependencyProperty.RegisterAttached("Attached" + propertyName,
                            typeof(T), typeof(object), new PropertyMetadata(callback));
        element.SetBinding(_dependencyProperty,  new Binding(propertyName) 
			{ Source = element });
    }
 
    public void Dispose()
    {
        _element.ClearValue(_dependencyProperty);
    }
}

The following is the class to use if all instances of the class bind to the same property. Be careful not to use this for two different controls from the using instance since there would be no way in this implementation to distinguish the two different properties.

public class AttachedDependencyPropertyStatic<T>
{
    DependencyProperty _dependencyProperty;
    string _propertyName;
 
    public AttachedDependencyPropertyStatic
	(string propertyName, PropertyChangedCallback callback)
    {
        _propertyName = propertyName;
        //Bind to a dependency property
        _dependencyProperty = DependencyProperty.RegisterAttached("Attached" + propertyName,
                            typeof(T), typeof(object), new PropertyMetadata(callback));
    }
 
    public void Add (FrameworkElement element)
    {
        Binding b = new Binding(_propertyName) { Source = element };
        element.SetBinding(_dependencyProperty, b);
    }
 
    public void Dispose(FrameworkElement element)
    {
        element.ClearValue(_dependencyProperty);
    }
}

Note that in both these cases, I use the typeof(object) instead of using the specific type. The typeof(object) works and means that the classes need to have less information passed.

Using the Code

I created a very simple application to demonstrate using the two DependencyProperty classes. In the main page, I attached to the “IsPressed” property of the Button, and then just displayed the date/time when the button was either last pressed, or released. This is a contrived example since it would be easy enough to capture the button up (MouseLeftButtonUp) and down (MouseLeftButtonDown) events. When I originally created this code, it was for a container, and I had to scan the contents for buttons and connected to the IsPressed DependencyProperty so I could change behavior when any button was pressed.

It is very easy to use either of these classes. The following shows how to use the instance version:

private AttachedDependencyProperty<bool> property1;

public MainPage()
{
    InitializeComponent();
    property1 = new AttachedDependencyProperty<bool>
		("IsPressed", TestButton1, IsPressedChangedCallback);
}

private void IsPressedChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    …
}

This is an example of how to implement the static version:

private static AttachedDependencyPropertyStatic<bool> property2 =
			            new AttachedDependencyPropertyStatic<bool>
					("IsPressed", IsPressedChangedCallbackStatic);
 
public MainPage()
{
    InitializeComponent();
    property2.Add(TestButton2);
}
 
private static void IsPressedChangedCallbackStatic(DependencyObject d, 
DependencyPropertyChangedEventArgs e)
{
    …
}

Points of Interest

If you look at the DependencyProperty code, you will see that I do not worry about the type of the owner when registering DependencyProperty. I use the object type. This does not seem to cause any problem, and I am not sure why Microsoft required the owner type.

Be sure that you are using the right type when attaching to a DependencyProperty. If the wrong type is used, then it appears that no events will be captured, and there will be no indication of why this is happening.

History

  • 15th June, 2011: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here