Attached Properties outside of WPF

Here I continue talking about re-implementing and improving WPF concepts outside of WPF and in a way that is not necessarily connected to GUI development. The first article in the series was discussing implementation of property and collection bindings outside of WPF and is available at Binding without WPF or at Codeproject: Binding without WPF. The final goal of these series is to implement most of the concepts that WPF introduced outside of WPF without dependency on any MS visual libraries and, perhaps, even in different languages, like JavaScript, Java and Objective-C.

This article discusses re-implementing WPF Attached Properties.

As a reminder – WPF Attached Properties serve the same purpose as the usual properties: they allow to get a property value from an object. They are, however, implemented very differently from the usual properties: instead of being part of the object, they are defined outside of it. If an Attached Property was set on an object, it can be retrieved from some memory store outside of the object with the object serving as a key. If an Attached Property was never defined on an object, the Attached Property’s default value will be returned. This value is defined per Attached Property and will be the same for any object.

WPF’s Attached Properties provide a number of very useful features:

  1. Attached Properties were used in WPF to reduce the storage required for all the numerious
    WPF properties. Indeed, if an object uses default Attached Property value, it does not
    require any additional storage. This type of storing property values
    is called sparse storage.
  2. WPF Attached Properties virtually allow adding new data to an object
    without modifying the object’s type.
  3. WPF Attached Properties allow setting callbacks that fire when a property
    changes on an object.
  4. In WPF only Attached (and Dependency) Properties can be a binding’s target.
    (Dependency Properties are very similar to the AttachedProperties but can
    only be defined within the type to which they can be attached).
  5. Attached Properties propagate down the visual tree.
  6. WPF’s built-in animation framework can only animate Attached and Dependency Properties.

Here we show how to build a framework providing capabilities very similar to the WPF Attached Properties. In order to differentiate between the WPF Attached Properties and this framework’s properties I call them AProperties or AProps. Here are the AProps capabilties that match those of the Attached Properties:

  1. AProps provide sparse storage.
  2. AProps allow to add external data to the objects without changing
    the objects’ type or class.
  3. AProps allow to add a callback to be fired when a value changes on an object.

On top of the features above that are also available for the WPF attached properties it will also provide the following nice features:

  1. AProps can be attached to a C# entity of any type, not only to those descended from DependencyObject.
  2. Unlike WPF Attached Properties, the mechanism of operating with AProps is strongly typed.
  3. In WPF one can specify an Attached Propertie’s callback when it is created or registered. This callback fires after an object’s property changes and it is the same for any object that has the Attached Property. AProps allow to specify also a callback to be fired before a property is changed on an object. If this callback returns false, the property changed is cancelled. Moreover, the framework allows adding callback to the individual objects. This callbacks are fired on when the AProperty is changed on the object to which the callback was added. Other objects are not affected by the callback.
  4. Unlike Attached Properties, AProps do not have to be defined as static variables (even though they can be defined static).

Now I would like to list the functionality that the Attached Properties have, but AProps (at this point yet) do not.

  1. The binding framework from the previous article is not made to bind to or from AProps. Even though this functionality is coming soon.
  2. Attached Properties change the property value within UI thread, while the AProps change in the thread of the caller. Perhaps, at some point, I’ll add another degree of freedom to the AProps that would allow to specify property change thread.
  3. There is no (yet) visual framework built around AProps, so there is no propagation down the visual tree or animation classes that use AProps.
  4. There is no value coercion mechanism for AProps (which anyways not used very frequently in WPF).

The AProps code together with the test project that shows how to use them is can be downloade from APropsCode.zip.

The main (virtually the only) class for dealing with AProps is AProperty under NP.AProps project.
It provides a functionality for creating AProperty object. AProperty contains a Dictionary (map) that maps the objects to their property values, or rather to some entities that contain their corresponding property values. If the object does not exist in the Dictionary, the default value gets returns as its AProperty value.

The central public methods of AProperty class are the following:


  1. public PropertyType GetProperty(ObjectType obj)

    Given an object returns its AProperty value.

  2. public void SetProperty(ObjectType obj, PropertyType newPropertyValue)

    Sets AProperty value on the passed object.
  3. public void AddOnPropertyChangedHandler
    (
        ObjectType obj, 
        OnPropertyChangedDelegate propChangedHandler
    )

    Adds object’s individual property change handler (other objects won’t be affected by it).

  4. public void RemoveOnPropertyChangedHandler
    (
        ObjectType obj,
        OnPropertyChangedDelegate propChangedHandler
    )

    Removes object’s individual property change handler.


  5. public void ClearAProperty(ObjectType obj)

    Clears AProperty value from the object (essentially removes the object from the AProperty‘s Dictionary.

The constructor of AProperty class has the following signature:

public AProperty
(
    PropertyType defaultValue = default(PropertyType),
    Func beforePropertyChangedFn = null, 
    OnPropertyChangedDelegate  onPropertyChangedFn = null
)

As you can see, it allows to pass the default value of the AProperty and two delegates: beforePropertyChangedFn and onPropertyChangedFn. The first of the delegates executes before AProperty changes on some object. If it returns false the property change is cancelled. The second delegate executes after the property change. Unlike individual object property change handlers these delegates execute for any object whose corresponding AProperty changes. The provided API does not allow modifying these delegate once they were set; otherwise all the objects that had their corresponding AProperty set might be affected. The OnPropertyChangedFn delegate is similar to OnPropertyChanged delegate that can be passed to the Attached Property’s metadata as the second argument.

The code that shows how to use AProperty API is under APropertyTest project. MyTestClass is a class with one property Name of type string. We want to add some integer index to MyTestClass object by using AProperty functionality.

Here is how we create indexAProperty object:

AProperty<MyTestClass, int> indexAProperty = new AProperty
(
    -1, // default indexAProperty value
    null, // no function is called before the property is set.
    (obj, oldVal, newVal) =>// to be called after the property is set
    {
        Console.WriteLine("This is a generic (not individual) property change event handler, oldValue: {0}, newValue: {1}", oldVal, newVal);
    } 
);

We set the default value for indexAProperty to -1, the generic delegate to fire before the property change is not set (null), and the generic post-property-change delegate prints a message with old and new value.

After that, we create a list of MyTestClass object and populate it with 3 objects. The indexAProperty for those 3 objects is set from is set to 1, 2 and 3 correspondingly. The object with index 2 is assigned an individual property change event handler:

// list to populate with objects
List myTestObjList = new List();

for (int i = 1; i < 4; i++)
{
    // create the object
    MyTestClass myTestObj = new MyTestClass { Name = "Obj " + i };

    // set an individual property change event handler for the second object
    if (i == 2)
    {
        indexAProperty.AddOnPropertyChangedHandler
        (
            myTestObj,
            (obj, oldVal, newVal) => // individual object delegate
            { 
                Console.WriteLine("This is individual property change event handler, oldValue: {0}, newValue: {1}", oldVal, newVal); 
            }
        );
    }

    // add an object to the list
    myTestObjList.Add(myTestObj);

    // set the indexAProperty on the myTestObj object to i
    indexAProperty.SetProperty(myTestObj, i);
}

Finally we iterate through the list of objects an print the indexAProperty value for each of the objects:

// print indexAProperty values for every object in the list
foreach (MyTestClass myTestObj in myTestObjList)
{
    int objNumber = indexAProperty.GetProperty(myTestObj);

    Console.WriteLine(objNumber);
}

One Response to “Attached Properties outside of WPF”

  1. Attached and Routed Events outside of WPF | Nick Polyak's Software Blog Says:

    […] Attached Properties outside of WPF I introduced a notion of AProperty – attached property implementation outside of WPF. Unlike […]

Leave a comment