April 27, 2008

Debugging Bindings with BindingManager

At last night's BFUG meeting Peter Farland demonstrated an undocumented class called BindingManager that is handy for debugging bindings in Flex. This class exists in both Flex 2 and Flex 3. Below is a quick example of how it is used:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" preinitialize="handlePreInitialize();">
<mx:Script>
<![CDATA[
private var _counter:int = 0;

private function changeText():void
{
    text1.text = "Changed Text " + _counter++;
}

private function handlePreInitialize():void
{
    BindingManager.debugBinding("text2.text");
}
]]>
</mx:Script>
<mx:Text id="text1" text="Initial Text"/>
<mx:Text id="text2" text="{text1.text}"/>
<mx:Button label="Update Text 1" click="changeText();"/>
</mx:Application>

The magic is the call to BindingManager.debugBinding(). The argument is the destination of the binding. The translation syntax is a little clearer if you aren't using an inline binding. For example the text2 binding above could also be written like this:

<mx:Text id="text2"/>
<mx:Binding source="text1.text" destination="text2.text"/>

Using either syntax, when the program is run I get the following output:

Binding: destString = text2.text, srcFunc result = Initial Text
Binding: destString = text2.text, error = TypeError: Error #1009: Cannot access a property or method of a null object reference.
Binding: destString = text2.text, srcFunc result = Initial Text
Binding: destString = text2.text, srcFunc result = Changed Text 0
Binding: destString = text2.text, srcFunc result = Changed Text 1
Binding: destString = text2.text, srcFunc result = Changed Text 2

The most interesting part of this output is the "TypeError" message. Normally errors like this are hidden from you on purpose by the binding mechanism. Now instead of having to set breakpoints in the framework it's possible to easily see just what is going on when a binding fires.

Tags: bindings flex

March 11, 2007

Programmatic Bindings

Recently I found myself refactoring an MXML component into an AS base class to make it easier to provide multiple views of the data. I wanted to factor as much of the code as I could into the base class including some of the bindings. While I've known for some time that Flex has programmatic binding support I'd not had a real use for it until now. The documentation is reasonable but I think I couple more examples would have helped. Below is a sample binding and the corresponding programmatic equivalent along with some gotchas.

Take an example MXML component that has a model Object set on it. The model exposes an enabled flag which the view needs to respond to in a complex fashion, beyond just disabling itself. If you want you can also replace the setter with a variable in these examples and it all works the same. It might look something like this (a bunch of code has been removed to focus just on the binding):

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
import com.example.Model;
[Bindable]
public var model:Model;

private function set controlsEnabled(controlsEnabled:Boolean):void {
    // logic to handle change
}
]]></mx:Script>
<mx:Binding source="model.enabled" destination="controlsEnabled"/>
<!-- child components removed -->
</mx:Canvas>

Now I want to turn that into an AS base class and not have every MXML subclass need to repeat the binding. My AS base class then looks like this:

package com.example {
    import mx.binding.utils.BindingUtils;
    import mx.containers.Canvas;

    public class SimpleBase extends Canvas {
        [Bindable]
        public var model:Model;

        public function SimpleBase() {
            super();
            BindingUtils.bindProperty(this, "controlsEnabled", this, ["model", "enabled"]);
        }

        public function set controlsEnabled(controlsEnabled:Boolean):void {
            // logic to handle change
        }
    }
}

While the view has changed to this:

<?xml version="1.0" encoding="utf-8"?>
<SimpleBase xmlns="com.example.*" xmlns:mx="http://www.adobe.com/2006/mxml">
<!-- child components removed -->
</SimpleBase>

The key to this magic is the BindingUtils.bindProperty() call in the base class. That mimics the mx:Binding that was previously in the MXML code. The documentation for BindingUtils uses site and host instead of source and destination which means you need to do a little translation when converting from one to the other. The most important thing to keep in mind when doing a translation like the one above is the use of this for both the site and host, otherwise you won't pick up all the changes.

The reason is that the bindings need to be anchored on some object that is issuing property change events. In the SimpleBase constructor model is null, so we can't really bind to that. Even if it wasn't null, the bindings would only fire off of the model instance that was active when it was setup. By chaining off of this you pick up changes both to model and the enabled flag within the model object.

The other tricky thing to notice is that the access modifier of the controlsEnabled() method changed from private to public. This needs to happen so that the code in BindingUtils can call it. Since this code is being executed outside of the class when in AS, instead of being compiled in (which is the case with MXML) it needs to be able to have standard access to the method. If you forget to make the setter public you'll get an error like this:

ReferenceError: Error #1056: Cannot create property controlsEnabled on SimpleView2.

The BindingUtils.bindSetter() method allows you to call an arbitrary method with the new value instead of just setting a variable (directly or via a setter).

Tags: bindings flex