Creating bindable, calculated read-only properties in Flex
Binding to read-only properties in Flex takes a bit more work than one might think at first glance. There are two basic types of read-only properties in Flex: "variable backed" and "calculated". Can you guess which one we're going to discuss here?
Lets start by clarifying exactly what we mean by read-only property. The most basic description is when you have a getter but no setter:
The variable backed version is when the getter is just a gatekeeper for a private variable:
public function get someValue():Number
{
return _someValue;
}
The calculated version is a bit more complex and is generally either a combination of values like first name and last name or a value that requires calculation. The example we'll use here is a totalTime property of a Playlist. The totalTime is calculated by adding together the duration of all songs in the playlist at any given time.
OK, how?
By default you will get a warning telling you that the binding on a read-only property will be ignored (and whatever you've bound to it will indeed not receive any updates). The first part of enabling the bindings involves using metadata. Everybody knows [Bindable], but what we're looking for is [Bindable(event="eventName")]. That will essentially tell the compiler to dispatch a PropertyChangeEvent for the property (which is how binding works) any time an event of type "eventName" is fired. That brings us to the next step of firing the event. In our scenario of a calculated read-only property this needs to happen when one/any of our inputs changes.
Going back to the playlist example, a good time to let everyone know totalTime has changed is when a song is added. For the sake of simplicity that is the only scenario this example covers. I won't bother talking through the code in the example because it is very straightforward.
View the example and view the source and you'll see this is all very simple. I have included the classes generated by the compiler as well in case anyone wants to dig around. The magic seems to be injected from _CalculatedBindableReadOnlyWatcherSetupUtil.as but I won't claim to have a very solid understanding of how that all works.
I hope this will shed some light for somebody out there and maybe even come in handy if you're stuck. Binding to variable backed read-only properties has been covered by numerous other people and I would recommend learning about that as well. Enjoy!


on March 7th, 2008 at 5:37 am
Just a word of caution, we're working on a huge flex project and one of our biggest performance issues is bindings triggering too often, simply because we've sometimes bound to methods with too many parameters or having too many events fire the PropertyChangeEvent on read only properties. Sometimes it's really tough to avoid those situations, but the trick is to keep it simple. Try to make sure the bindings only trigger when they actually need to instead of trying to cover as much ground as possible. Having a binding triggering 15 times when the data only changes once is just redundant.
Great blog post, it's nuggets like these that really help developers! Thanks!
on March 7th, 2008 at 9:54 am
Very good point Marcus. I probably should have included a disclaimer that making too many things bindable can eventually slow an app down. If you have more than one or two inputs into your calculation, or you have a lot of read-only properties I would probably suggest implementing a method to handle manual updates of the property and removing the bindings. Marcus have you optimized all of the binding triggers by checking to make sure the new value is actually different than the existing one before you dispatch the changed event?
on April 20th, 2008 at 3:45 pm
Hi Ben,
i used your approach but made it (imho) a bit more flexible. Maybe you can look at the code and let me know what you think!
thanx for the always interesting posts.
Arnoud
package sample
{
import flash.events.Event;
import flash.events.EventDispatcher;
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
public class Playlist extends EventDispatcher
{
public function Playlist() {
// needed if _songs is initialized before constructor is called.
if (_songs) _songs.addEventListener(CollectionEvent.COLLECTION_CHANGE, update, false, 0, true);
}
// getters / setters
private var _songs:ArrayCollection = new ArrayCollection();
public function set songs(value:ArrayCollection):void {
if (_songs) _songs.removeEventListener(CollectionEvent.COLLECTION_CHANGE, update);
_songs = value;
_songs.addEventListener(CollectionEvent.COLLECTION_CHANGE, update, false, 0, true);
}
public function get songs():ArrayCollection {
return _songs;
}
// now we have much more control and the event dispatching is centralized
// we can decide here on which collection events we want to update etc.
// alse we can check what properties of the collection are changed etc.
private function update(event:CollectionEvent):void {
if (event.kind == CollectionEventKind.ADD || event.kind == CollectionEventKind.REMOVE) {
dispatchEvent( new Event( "totalTimeChanged" ) );
}
}
// no more event dispatching here, the collection listener handles it all
public function addSong( song:Song ):void
{
songs.addItem( song );
}
// tell the compiler which property to check/update when a totalTimeChanged event fires
[Bindable(event="totalTimeChanged")]
public function get totalTime():Number
{
var time:Number = 0;
for each( var song:Song in songs )
{
time += song.duration;
}
return time;
}
}
}
on May 20th, 2008 at 6:50 pm
Ben: Well, sort of. The thing I forgot to mention was that bindings are really costly when they result in frame updates (which they usually do when sprinkled across mxml-files). The actual calling of functions and processing of data isn't all that bad performance wise, it's negligable at least in our cases.
Now, to avoid frame updates I have a few tips:
1. Know thy component. Try things to see when and why an update occurs. Some components might be poorly written which may make them update even though the data sent to it is the same.
2. Avoid triggering read only properties or methods too often. If you're triggering bindings when the actual value of the property haven't changed, you might be in for a world of pain if that property affects some heavy component such as a list or grid.
3. If you bind to a method which takes parameters, try to factor in the current value of the component your affecting as well. That way, you may return the original value which hopefully won't update (remember tip #1). You might be able to make use of flex's internal error handling which swallows exceptions occuring in bindings (I read this somewhere), but I don't recommend this at all. It's poor programming practice. Exceptions are exceptions, not a rule.
4. This one goes hand in hand with the previous tip. Duck out early. If some parameter is wrong or just crazy, return the original value as soon as possible so as not to perform unnecessary calculations.
5. Take my tips with a truckload of salt and make your own judgements with the profiler in hand.
PS. Sorry for replying so late.