Thursday, August 13, 2009

CFUnited Materials

My CFUnited powerpoint slides are here.

And the application I wrote during my session, along with the necessary helper classes, is here.

Sunday, August 2, 2009

Custom Flex Binding with PropertyChangeEvent

In Flex we have a very powerful tool in the [Bindable] metadata tag. However, it can also be a huge source of inefficiency. Basically, when you mark a property as [Bindable] the compiler adds code that dispatches a generic PropertyChangeEvent typed "propertyChange" when the property changes. Whenever this event is detected, the bindings on your property are evaluated. This is all good and fine until you have a bunch of properties that are all marked as [Bindable]. Now the player is watching for the same event type ("propertyChange") for all your bindings for all your properties. So any time one of them changes the player has to look through all the properties in your class to find the one that actually needs to have its bindings reevaluated. Very inefficient.

Thankfully, the people at Adobe at least gave us a way to work around this CPU deathtrap. You can mark your bindings to evaluate on a custom event type like this: [Bindable('myCustomEventType')]. If you use this wisely then the player will know exactly which property to check out when it detects the event, moving the binding evaluation time back from O(n) to constant time. Much better.

In past projects I've used this method with generic fash.events.Event objects to great success. However, on my current project Jonathan Branam, the lead architect, wanted to be more "correct" and use custom PropertyChangeEvents. Okay, makes sense, should be no problem, right? Well, it was a small problem. So, I set up my custom binding like this:

/**
* The current number of bytes loaded by the server during upload.
*/
protected var _bytesLoaded:int = 0;
[Bindable('bytesLoadedChanged')]
/** @copy #_bytesLoaded **/
public function get bytesLoaded():int{
return _bytesLoaded;
}
public function set bytesLoaded(value:int):void{
if(_bytesLoaded != value){ // only set the value if its different
var oldValue:int = _bytesLoaded;
_bytesLoaded = value;
dispatchEvent(new PropertyChangeEvent('bytesLoadedChanged',
false, false, PropertyChangeEventKind.UPDATE,
_bytesLoaded, oldValue, value, this));
}
}

Then I ran my code and the bindings didn't evaluate. It looked right to me, so what was the problem? I looked at the documentation for PropertyChangeEvent (duh, probably should have been a first step) and found this for the "property" property: "A String, QName, or int specifying the property that changed." So, the "property" property of the PropertyChangeEvent should be the NAME of the property that changed, not a reference to that property (follow that?). That amounted to changing three characters, instead of:

dispatchEvent(new PropertyChangeEvent('bytesLoadedChanged',
false, false, PropertyChangeEventKind.UPDATE,
_bytesLoaded, oldValue, value, this));

I now have:

dispatchEvent(new PropertyChangeEvent('bytesLoadedChanged',
false, false, PropertyChangeEventKind.UPDATE,
'bytesLoaded', oldValue, value, this));

And now my bindings work! For some reason having that property set incorrectly was preventing the player from identifying that I had dispatched a binding event. My guess is that it silently was causing an exception in the PropertyChangeEvent constructor, but I'm not sure. What I do know is that now my bindings work, they're efficient, and they're "correct." And I'm happy.