Archive for September, 2010

Data binding 101 (flex gumbo)

September 19, 2010
  • Data binding is the plumbing (or “out-of-the-box” feature) provided by modern presentation frameworks such as Flex SDK and WPF. The general idea is to facilitate communications concerning value changes among different objects, so that the application developers can focus on higher level tasks. Most of these plumbings are optimized for declarative code (MXML, XAML and metadata tags in your procedural code). For example, if you use [Bindable],<fx:Binding> meta tags or curly braces (MXML data binding expressions), Flex compiler will give compilation warnings about whether it thinks the data bindings you set up will work nor not; while you don’t get this kind of nice friendly reminder if you use mx.binding.utils.BindingUtils or mx.binding.utils.BindingUtils.ChangeWatcher directly.
  • Data binding is nothing magical than using event listeners to watch for changes on objects/values and then get the updated values. There are two sides of the equation:A. You tell the compiler which object/value you’d like to watch, like so:[Bindable(event="selectedEmployeeChanged")]
    public function get selectedEmployee():Employee
    {
    return _selectedEmployee = v;
    }

    Here you tell the Flex to get the value for _selectedEmployee whenever the interested parties are notified by the event “selectedEmployeeChanged”.  If you don’t specify an event name, flex will automatically associate this data bound value with an event named “propertyChange” and will dispatch that event for you behind the scene. This is convenient for the developer as you don’t need to care about when to fire the event(s) and which event(s) you need to listen for the involved parties, etc..

    B. You tell the compiler when the object/value is changed, like so:
    public function set selectedEmployee(v:Employee)
    {
    if(!v.equals(_selectedEmployee))
    {
    _selectedEmployee = v;
    dispatchEvent(new Event("selectedEmployeeChanged", true, true));
    }
    }

    Here you trigger the data binding (chain) by firing the “selectedEmployeeChanged” event which you defined in step A. Flex then will copy the values for you to the interested parties (destinations of the data binding.)

  • To really understand data binding, it’s important to understand under what conditions the compiler will do the work for you (or the opposite, “when data binding is not supported”) when you set up your data binding (using <fx:Binding>, [Bindable] and data binding expressions). It helps to give examples to show when it doesn’t work. Scenario: You want to watch property changes in another class. For example, if I have an object called FacebookStatus and it contains properties such as numberOfLikes and numberOfDislikes (a feature pending further proof by the product manager) and I have another object called UserInfo which contains a FacebookStatus object. If my FacebookStatus object wants to watch changes of numberOfLikes of FacebookStatus, I can’t simply say:
    <!--In FacebookStatus-->
    public function set numberOfLikes(v:int)
    {
    _numberOfLikes = v;
    dispatchEvent(new Event("statusChange"));
    }

    <!--In UserInfo-->
    [Binding(type="statusChange", type="flash.events.Event")]
    public function handleUserStatusChange(event:Event):void
    {
    //do something to react...
    }

    In the example above, UserInfo will never gets notified by “statusChange” as the compiler won’t look into another object to make the [Binding] tag work. In this case, you can do the binding work yourself by using mx.binding.utils.BindingUtils.bindSetter().
  • It helps to understand what the data binding doesn’t do as well as the trade-offs of having the compiler doing the data binding for you. Sometimes it’s better to do the work yourself. For example, if you have a Watch object that works somehow like a timer (stores information about the minutes elapsed since certain point of time) and you want to show it on the screen, you have to call a function to constantly get the minutes value (e.g., in a loop) in order to get the UI updated constantly. Merely setting up the data binding from the Watch object to the UI is not sufficient.Also, since the compiler needs to create a lot of listeners under the hook, it can consume much more memory and involves deep function stack calls to eventually get the values copied around.
  • If you set up your data binding in <fx:Binding> tags or using [Bindable] metadata tags, Flex sets up the data binding at the “initialization” stage of the UIComponent’s life cycle. It uses try/catch to hide the null objects/values errors and update the values when available.
  • If you have a data object with complex properties, which has complex properties, etc., (e.g, employee.supervisor.department.id) and you want to data bind it’s property, then each node in the data binding chain needs to be set up as data bindable. This won’t automatically happen; but the compiler will give a friendly warning about it if it finds any of the properties in the chain is not data bound.
  • You can watch for more than one event on a value change:[Bindable("change")]
    [Bindable("valueCommit")]
    public function get selectedIndices():Vector.<int>
    {
    return _selectedIndices;
    }

Here are several interested read (last one in Chinese):
Two-Way Data Binding in Flex 4
Michael Labriola’s presentation entitled Diving in the Data Binding Waters
Flex 数据绑定易犯的错误:普遍的误用和错误

Custom (spark) item renderer and component

September 13, 2010

For whatever reason, you want to extend UIComponent (or any of the subclasses) or ItemRenderer, you can do it in either ActionScript or MXML.

In ActionScript, you do it like this:

//class definition. The file name is "MyItemRenderer.as"
public class MyItemRenderer extends ItemRender {//stuff here...}

In MXML, you do it like this:

<?xml version="1.0" encoding="utf-8"?>
<!--The base class is the top tag. The file name is  "MyItemRenderer.mxml"-->
<mapping:BaseMarker xmlns:fx="http://ns.adobe.com/mxml/2009" ...>...</mapping:BaseMarker>

Flex automatically generates an ActionScript class of the same name as your .mxml file name when you compile.

If you extend your ItemRenderer or UIComponent subclass using MXML, you can do the same things as you extend them using ActionScript, such as assign a property, or override a protected or public member. It’s often obvious how you override the methods, as these happen in the <s:Script> block(s). It took me a while to get used to the fact that I can simply assign properties (public var in ActionScript) right on the top tag (where you declare the base class/tag).

Below is an example: The idea was to use a base marker used for a map and then have different flavors of markers; but in the real project, I decided I would rather use different itemRenderers instead of subclassing the base one, as the latter seems more convenient for the scope of the project.

<!–base marker: BaseMarker.mxml–>

<?xml version=”1.0″ encoding=”utf-8″?>
<s:ItemRenderer xmlns:fx=”http://ns.adobe.com/mxml/2009&#8243;
xmlns:s=”library://ns.adobe.com/flex/spark”
xmlns:mx=”library://ns.adobe.com/flex/mx”
autoDrawBackground=”true”>
<fx:Script>
<![CDATA[
import com.mh.mapdispatch.model.Location;
[Bindable]
public var iconSource:Class;
[Bindable]
public var labelText:String = “”;
[Bindable]
public var displayLabel:Boolean = false;
[Bindable]
public var labelColor:uint = uint(“0×000000″);
[Bindable]
public var labelBorderColor:uint = uint(“0×000000″);
[Bindable]
public var labelBackgroundColor:uint = uint(“0xffffff”);

private var _latitude:Number;

[Binable]
public function get latitude():Number
{
return _latitude;
}
public function set latitude(value:Number):void
{
_latitude = y = value;
}

private var _longitude:Number;
[Binable]
public function get longitude():Number
{
return _longitude;
}
public function set longitude(value:Number):void
{
_longitude = x = value;
}

]]>
</fx:Script>
<!– Flex data binding will not able to detect assignments to latitude/longitude
<fx:Binding source=”latitude” destination=”y” twoWay=”true”/>
<fx:Binding source=”longitude” destination=”x” twoWay=”true”/>
–>
<s:VGroup>
<mx:Image id=”markerIcon” source=”{iconSource}”/>
<s:BorderContainer id=”labelBorder”
visible=”{displayLabel}”
includeInLayout=”{displayLabel}”
borderColor=”{labelBorderColor}”
backgroundColor=”{labelBackgroundColor}”>
<s:Label id=”markerLabel”
text=”{labelText}”
color=”{labelColor}”/>
</s:BorderContainer>
</s:VGroup>
</s:ItemRenderer>

<!–subclass itemrenderer: CameraMarker.mxml–>

<?xml version=”1.0″ encoding=”utf-8″?>
<mapping:BaseMarker xmlns:fx=”http://ns.adobe.com/mxml/2009&#8243;
xmlns:s=”library://ns.adobe.com/flex/spark”
xmlns:mx=”library://ns.adobe.com/flex/mx”
xmlns:mapping=”com.mh.mapdispatch.infrastructure.mapping.*”
autoDrawBackground=”true”
displayLabel=”true”
labelText = “{data.location}”
latitude = “{data.location.latitude}”
longitude =”{ data.location.longitude}”>
<fx:Script>
<![CDATA[
override public function set latitude(value:Number):void
{//do stuff here...}
]]>
</fx:Script>
</mapping:BaseMarker>

This one is pretty interesting, in terms of how to use MXML to create itemRenderers.


Follow

Get every new post delivered to your Inbox.