Click here to Skip to main content
Click here to Skip to main content

Class to Support Attaching to Dependency Property

, 16 Jun 2011
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Clifford Nelson
Software Developer (Senior) ETeam/Delloitte
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last three years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with NBC Universal in Universal City, CA

Comments and Discussions

 
GeneralI think you are just tried to monitor a DP value changing right PinmvpSacha Barber16-Jun-11 2:44 
GeneralRe: I think you are just tried to monitor a DP value changing right PinmemberPaul Selormey16-Jun-11 12:50 
GeneralRe: I think you are just tried to monitor a DP value changing right PinmvpSacha Barber17-Jun-11 4:10 
GeneralRe: I think you are just tried to monitor a DP value changing right PinmemberClifford Nelson17-Jun-11 5:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140902.1 | Last Updated 16 Jun 2011
Article Copyright 2011 by Clifford Nelson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid