Most examples you see with Flex do data binding in MXML. This seems to suffice for most cases I can think of, but recently I found the need to change a binding dynamically at run time.
At the time the only way I could think of doing this was to was to do it in ActionScript. I didn't know how to do that, and the documentation seemed a little terse on this topic. So here's my little ditty on how to bind (and "unbind") data in ActionScript.
As a side note, I didn't use this in my application. Instead I used an interim variable to bind my custom control to. I stored my actual data in an array of arrays. Then, in my Cairngorm Command class I copied the appropriate array element into the actual variable that was bound to my custom control. But since I haven't posted anything here for a while, I thought I'd post this article about performing binding operations in ActionScript...
You can use the BindUtils class, which provides two static methods for performing data binding operations: bindProperty() and bindSetter(). I used the bindProperty() method. Here's some example code, and my explanation of what it does:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="init()" viewSourceURL="BindExampleSrc/index.html">
<mx:Form >
<mx:FormHeading label="ActionScript Binding Example" />
<mx:FormItem>
<mx:Label id="labelTextInput1" text="Text Input 1 (original binding source)" />
<mx:TextInput id="textInput1" />
</mx:FormItem>
<mx:FormItem>
<mx:Label id="labelTextInput2" text="Text Input 2 (original binding destination)" />
<mx:TextInput id="textInput2" />
</mx:FormItem>
<mx:FormItem>
<mx:Label text="Click this button to reverse the source/dest of the binding" />
<mx:Button label="Reverse Bindings" click="reverseButtonHandler()" />
</mx:FormItem>
</mx:Form>
<mx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
import mx.binding.utils.BindingUtils;
private var changeWatcher:ChangeWatcher;
private var bindToggle:Boolean = true;
private function init():void {
changeWatcher = BindingUtils.bindProperty(textInput2, "text", textInput1, "text");
}
private function reverseButtonHandler():void {
// undo the data binding
changeWatcher.unwatch();
if (bindToggle) {
changeWatcher = BindingUtils.bindProperty(textInput1, "text", textInput2, "text");
bindToggle = false;
labelTextInput1.text = "Text Input 1 (now the binding destination)";
labelTextInput2.text = "Text Input 2 (now the binding source)";
}
else {
changeWatcher = BindingUtils.bindProperty(textInput2, "text", textInput1, "text");
bindToggle = true;
labelTextInput1.text = "Text Input 1 (now the binding source)";
labelTextInput2.text = "Text Input 2 (now the binding destination)";
}
}
]]>
</mx:Script>
</mx:Application>
The function signature for the bindProperty has confusing argument names for the uninitiated:
public static function bindProperty(site:Object, prop:String, host:Object, chain:Object, commitOnly:Boolean = false):ChangeWatcher
But it's not that bad:
- The first argument "site" is the destination object, the object that we're binding data to.
- The second argument "prop" is a string that contains the name of the property we are binding data to.
- The third argument "host" is the object that is the source of the binding.
- The fourth argument "chain" is a string that contains the name of the property on the binding source object.
- The fifth argument is a boolean, which provides finer grain control over when to update the binding destination, and you probably won't need it for most situations.
- The
bindProperty()method returns aChangeWatcherobject, we can use this object to undo the data binding.
Note that the BindingUtils class also has a bindSetter method, which should be used when the binding destination object has a setter method to set the value of the desired property.
Here's the simple app listed above, you can also right click on it to the Flex source code. When the app is initialized, the value of the 1st text input field is bound to the second text input. If you click the "Reverse Bindings" button, the data binding is undone and then performed in the opposite direction. Simple, but you should get the point. Cheers!
You may be interested in my BindageTools library:
ReplyDeletehttp://code.google.com/p/bindage-tools/
We use a builder API to set up data bindings, and provide some simple hooks for tearing down bindings when you're done.
Bind.fromProperty(textInput1, "text")
.toProperty(textInput2, "text");
Cleanup is simplified as follows:
public function setUpBindings() {
changeWatchers =
BindTracker.collect(createBindings);
}
public function tearDownBindings() {
unwatchAll(changeWatchers);
}
private function createBindings() {
Bind.fromProperty(foo, "text")
.toProperty(bar, "text");
Bind.twoWay(
Bind.fromProperty(textInput1, "text"),
Bind.fromProperty(textInput2, "text"));
}
If you're using Swiz, we also have an annotation processor which takes care of the setup/teardown lifecycle for you:
[DataBinding]
public function createBindings() {
// Any bindings created here will
// automatically be cleaned up when the
// bean/view is torn down.
}
You wrote this tip 4 years ago, but I'm thanking you now for doing that, because this tip is absolutely brilliant and just solved my problem :-)) Very, very nice. Really appreciate it.
ReplyDeleteErik