Menu

Conventions for Bound Properties

SourceForge Editorial Staff

Summary


By default properties should not be marked [Bindable]. In other words, one should not get in the habit of defining bindable properties without considering the implications of doing so.

One should define a property to be bindable if it's likely that instances of other classes will need to stay in sync with the property's value. Properties in a class that's intended to serve as a model are typically bindable for this reason.

Staying in sync with a property from within a subclass should be handled by overriding the properties set method. In general, properties should be defined with get/set methods, and not with a public var.

Bindable properties should dispatch change events manually, by specifying the [Bindable] event parameter, and by calling dispatchEvent(). The event parameter, aka the change event's "type string", should have a present tense verb suffix, typically "Change" if the event is to be public, past tense "Changed" otherwise.

Loose Coupling


Bindable properties in Flex enable loose coupling. An event will be dispatched after a property has been changed. This may happen synchronously, before the property's set method returns, or it may be deferred until commitProperties() time, or in some cases even later. By the time the event is dispatched, the property may have been reset more than once.

Getting the value of a property will always return the most recently set value. The only way to tightly couple to a property is to override the property's set method.

The order in which Bindable property events are dispatched is not guaranteed. If an application sets property "a" and then property "b", there's no guarantee that the property change events will be dispatched in the same order.

Note also: a few Halo components provide synchronous change notifications and they advertise as much with the [NonCommittingChangeEvent("event-name")] meta tag. Use of "NonCommittingChangeEvent" is atypical.

The MXML [Bindable] MetaData Tag


The MXML [Bindable] metadata tag instructs the compiler to emit code that will intercept changes to the following property and dispatch PropertyChangeEvents.

The class that contains [Bindable] tags must be an EventDispatcher, like a UIComponent subclass. Here's an example of one property definition:

private var _foo:Object = null;

<a href="Bindable">Bindable</a>
public get foo():Object { return _foo; }
public set foo(value:Object):void { _foo = value; }

The code generated by the MXML compiler dispatches an event each time
the foo property changes - after the property changes - like this:

var event:PropertyChangeEvent = new PropertyChangeEvent();
event.source = this;
event.type = PropertyChangeEvent.PROPERTY_CHANGE;
event.kind = PropertyChangeEventKind.UPDATE;
event.propertyName = "foo";
event.oldValue = oldValue;
event.newValue = _foo;
dispatchEvent(event);

A PropertyChangeEvent utility method does most of the work, the code that's actually generated is just:

dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, "foo", oldValue, value));

This is the simplest way to define Bindable properties. Doing so can introduce some semantic problems for properties that are not just glorified vars.

Semantic Problems with the [Bindable] MetaData Tag


When the [Bindable] tag is used with no "event" argument, as in the previous section, the linkage generated by the MXML compiler to intercept property changes can generate "false" propertyChangeEvents, i.e. events that incorrectly characterize a change, or don't correspond to a property value change at all.

private var _foo:Object = "FOO";

<a href="Bindable">Bindable</a>
public get foo():Object { return _foo; }
public set foo(value:Object):void 
{ 
    if (value)
        _foo = value.toString();
}

foo = null;
// PropertyChangeEvent incorrectly reports "FOO" => null.   In 
// this case there's no change to the foo property.

foo = <a href="1%2C2%2C3">1,2,3</a>
// PropertyChangeEvent incorrectly reports "FOO" => <a href="1%2C2%2C3">1,2,3</a> In 
// this case the new value should be a string "<a href="1%2C2%2C3">1,2,3</a>".

Another common semantic problem with the auomatically generated Bindable property change linkage occurs with Flex components, when the the property is reset at commitProperties() time.

In this case we (Flex) create our own complex linkage which can cause a single request to set a property to udpate other properties as a side effect. In that case we only want to dispatch one PropertyChangeEvent, and only after the other properties have been updated. This can't really be done with automatic properyChange event dispatching. The boilerplate section below provides a template for doing the work manually.

Performance Problems with the [Bindable] MetaData tag


Although folklore has it that the performance of automatically generated PropertyChangeEvent dispatching is poor, this doesn't seem to be the case. The execution time performance of manual event dispatching has a little less than a 2% advantage over automatic event dispatching. Similarly there's no significant difference for the runtime footprint of an automatically generated PropertyChangeEvent dispatch, versus the same code written by hand. This should come as no surprise, since the code one would write by hand is nearly identical to what the MXML compiler emits.

There is a significant per property, per instance, runtime footprint cost for binding to a [Bindable] property. The incremental per instance footprint of a single property is just the cost of the backing slot and its value. For example a Number valued slot costs 8 bytes. The footprint cost of creating a binding to that property is two orders of magnitude more. So one should consider creating bindings carefully, since their runtime footprint cost is considerable.

There is also a significant execution cost for updating "propertyChange" bindings. There's a subtle difference between "propertyChange" bindings and bindings to properties with a custom value for the [Bindable] event parameter. A propertyChange binding must filter all PropertyChangeEvents to select those with a matching propertyName, a custom event binding does not.

<a href="Bindable">Bindable</a> var p1;
<a href="Bindable">Bindable</a> var p2;

<a href="Bindable%28event%3D%26quot%3Bp3Change%26quot%3B%29">Bindable(event="p3Change")</a> var p3;
<a href="Bindable%28event%3D%26quot%3Bp4Change%26quot%3B%29">Bindable(event="p4Change")</a> var p4;

Per the example above, the code generated for bindings to p1 or p2, must filter PropertyChangeEvents for both properties, where bindings for p3 or p4 would not.

The cost of updating PropertyChangeEvent bindings like p1,p2 is significantly higher than p3,p4 as one would expect. It's O(N), where N is the number of properties, rather than just constant time (some benchmarks indicate that the performance degrades even faster than O(N)).

So, to summarize, the [Bindable] event parameter should be used to specify the event's type string because updating bindings to properties defined this way can be much faster. And one should be cautious about creating bindings, because the runtime footprint of bindings can be 100X larger than the property itself.

Bindable Property Boilerplate, Private Bindable event


The two templates that follow cover the most common use cases for Bindable properties. In both cases the Bindable event, whose past tense name is typically just propertyNameChanged, isn't specified with an [Event] metadata tag, and so the event name is considered to be essentially private. The event name should be public if it's likely that a developer would want to write a AS handler for the event. That version of the template is slightly more complicated and is covered in the next section.

In the first template event dispatching has been deferred until commitProperties time, which is typical.

private var _foo:Object = "default value";
private var fooChanged:Boolean = false;

<a href="Bindable%28event%3D%26quot%3BfooChanged%26quot%3B%29">Bindable(event="fooChanged")</a>

/**
 *  <a href="explain%20what%20the%20property%20means">explain what the property means</a>
 * 
 *  This property is Bindable, it dispatches "fooChanged" events.
 * 
 *  @default "default value"
 */
public function get foo():Object 
{ 
    return _foo; 
}

/**
 * @private
 */
public function set foo(value:Object):void 
{ 
    if (_foo == value)
        return;

    _foo = value;  
    fooChanged = true;
    invalidateProperties();
}

/**
 * @private
 */
override protected function commitProperties():void
{
    super.commitProperties();
    if (fooChanged)
    {
        // update side effected properties
        fooChanged = false;
        dispatchEvent(new Event("fooChanged"));
    }
}

This boilerplate is intended to be lightweight. It does not convey the source object or the property's old/new values (more about that in the following section) and it has one notable flaw. If the value of foo is changed, and then reset to its original value before commitProperties() runs, a change event is still dispatched. This is undesirable but tolerable. The cost of preventing it (caching the old property value) doesn't balance the benefit.

The following template is for the simplest case of bound property notification. Events are dispatched synchronously, before the set method returns. This version is sufficient when the property's set method doesn't side effect the value of any other properties.

private var _foo:Object = "default value";

<a href="Bindable%28event%3D%26quot%3BfooChanged%26quot%3B%29">Bindable(event="fooChanged")</a>

/**
 *  <a href="explain%20what%20the%20property%20means">explain what the property means</a>
 * 
 *  This property is Bindable.
 * 
 *  @default "default value"
 */
public function get foo():Object 
{ 
    return _foo; 
}

/**
 * @private
 */
public function set foo(value:Object):void 
{ 
    if (_foo == value)
        return;
    _foo = value;  
    dispatchEvent(new Event("fooChanged"));
}

The boilerplate in the next section is similar, it's for events whose names are intended to be public.

Bindable Property Boilerplate, Public Bindable event


The template below covers defining a Bindable property whose event is public. Only the synchronous event dispatching case is covered.

In this template the event's name is present tense, "fooChange", and it's specified with an [Event] metadata tag, and in the get method's ASDoc.

The comment and [Event] metadata tag below should appear near the top of the class definition. Typically above the class ASDoc and after the import statements.

/**
 *  Dispatched when the foo property changes.
 */
<a href="Event%28name%3D%26quot%3BfooChange%26quot%3B%2C%20type%3D%26quot%3Bflash.events.Event%26quot%3B%29">Event(name="fooChange", type="flash.events.Event")</a>

The rest of the template is the usual property definition. The main difference from the private Bindable event version above, is that the event name is present tense.

private var _foo:Object = "default value";
private var fooChanged:Boolean = false;

<a href="Bindable%28event%3D%26quot%3BfooChange%26quot%3B%29">Bindable(event="fooChange")</a>

/**
 *  <a href="explain%20what%20the%20property%20means">explain what the property means</a>
 * 
 *  This property is Bindable, it dispatches "fooChange" events.
 * 
 *  @default "default value"
 */
public function get foo():Object 
{ 
    return _foo; 
}

/**
 * @private
 */
public function set foo(value:Object):void 
{ 
    if (_foo == value)
        return;

    _foo = value;  
    fooChanged = true;
    dispatchEvent(new Event("fooChange"));
}

The next section covers the rationale behind some of templates' details.

Background


Naming Conventions

The tense convention for private/public Bindable event names, is consistent with earlier versions of Flex and well known event names like "propertyChange" and "dataChange".

Event not PropertyChangeEvent

The boilerplate uses a simple flash.events.Event, rather than a mx.events.PropertyChangeEvent. Doing so has some advantages:

  • There's no need to record the property's old value. Doing so would require an extra private slot and in some situations would cause an undesirable short term leak (e.g. if the value of the property was a potentially large image). Listeners rarely require the old and new values and those that do can cache the current value of a property themselves.

  • Creating one is simpler. To create a PropertyChangeEvent whose type was "fooChange" one would have to write:

    dispatchEvent(new PropertyChangeEvent("fooChange", false,
    false, PropertyChangeEventKind.UPDATE, "foo", oldValue, foo, this));

A simple event is sufficient for binding. If another event type is used, like a PropertyChangeEvent, then the ASDoc should say as much.

Bindable Event Parameter Jeremiad


I wrote this in August (2008) with subject "What's private var Group::scaleGridChanged:Boolean for, and why I just can't stop weeping". The impetus was the experience I'd had trying to learn what the meaning of the event name Binding parameter was by reading the documentation. Although the material that follows isn't the least bit constructive, it may be worth considering before we grow the scope of our recommendation about using the [Bindable] event parameter from the Flex SDK team, to Flex developers in general.

[[ quote ]]
... Similarly, all of the scaleGrid properties explicitly dispatch a PropertyChange update event. This is confusing on a bunch of levels for yours truly. According to the Flex3 developers guide (page 1249), the syntax for the Bindable annotation is this:

<a href="Bindable%28event%3D%26quot%3Beventname%26quot%3B%29">Bindable(event="eventname")</a>

And the default value of the event parameter is "propertyChange". This is confusing because PropertyChange events don't have a "name". They have a "kind", a "type", and a "propertyName".

Perhaps the event parameter is the propertyName? This doesn't make sense, since it should be possible to set the propertyName automatically. The value of "kind" is an enumeration (I'm hoping kind == PropertyChangeEventKind.UPDATE is used) so the event parameter can't be that. So maybe the event argument is actually the event's type? The API doc for PropertyChangeEvent helpfully explains that the value of type is "The type of event". Excellent. The doc for PropertyChangeEvent.PROPERTY_CHANGE notes that it's the type of PropertyChangeEvents. But the PropertyChangeEvent constructor requires a type parameter which is documented with "indicates the action that triggered the event". Doesn't the type have to be the aforementioned constant?

To further confuse matters, page 250 points out that, if the Bindable event parameter isn't specified:

"The Flex compiler automatically generates an event named propertyChange, 
of type PropertyChangeEvent"

The value of the PROPERTY_CHANGE type constant is "propertyChange" so I'm guessing what this is supposed to mean that a PropertyChangeEvent event whose type property is "propertyChange" is generated.

So the net of this is that if you specify the Bindable event name, using the event parameter, you're actually specifying the event's type, which is a string that doesn't actually name the event's type.

I think I'm going to have a good cry now.
[[ /quote ]]


Related

Wiki: Flex 4

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.