Archive for the ‘ActionScript’ Category

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(“0x000000″);
[Bindable]
public var labelBorderColor:uint = uint(“0x000000″);
[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.

Autohide UIScrollbar on a dynamic text field – AS3

February 22, 2010

mUIScrollBar.visible = (mTextField.maxScrollV > 1);

“Error #1088: The markup in the document following the root element must be well-formed.”

December 5, 2009

I was using PHP and curl as proxy to get some xml files from Basecamp for my Flex UI to consume and was successful (something like “return $response   = curl_exec($session);“. But after I refactored my little php functions into an object/class and included it in another delegation file, it suddenly stopped working. In Flex, I got the following error for the HTTPService fault event:

faultCode:Client.CouldNotDecode
faultString:’Error #1088: The markup in the document following the root element must be well-formed.’
faultDetail:’null’

In Firefox 3.5, the output XML file looked ok. But both Safari and IE gave error messages: There was an “*” in front of the <?xml ..?> header in Safari source code view; while IE gave the following error:

Invalid at the top level of the document. Error processing resource

After comparing the files before refactoring and the one after, I noticed that the refactored one was encoded in UTF-8 while the old one was in ANSI. After I converted my class file into ANSI, XML output became normal in all three browsers.

BTW, I used Notepad++. Not sure if that was an issue.

Anyhow, lessons learned: Make sure the source files are encoded in ANSI.

Any comments and thoughts are welcome!

Block statements in control flow constructs

December 2, 2009

There is no need to block (using braces to wrap) single line statements in a control flow (as-if, do-while, for/while); and you can mix blocks and non block statements together like this:

if(num > 0)
trace(num+” is positive;\n”);
else if(num <0)
{
trace(num+” is negative.”);
trace(num+” don’t be negative;\n”);
}
else if(num == 0)
trace(num+” is neutral;\n”);

This applies to all the control statements although the example here is just of if/else .

With this being said, The Elements of C# Style recommends Always us block statements in control flow constructs. Reason one is that it makes it easy to add additional statements in it; reason two is it’s easier to read than those without blocks.

Accessing UI views, properties and methods in main app in Flex

July 6, 2009

This applies to both AIR and Flex project, in Flex SDK 3.3:

// Application.application refers to the top level application.
// But be aware that it's typed to Object as opposed to Application.
// According to Flex team, it's for the purpose of fast compilation and
// being able to access properties/methods in the <Script> of their
// <Application> without having to cast it to their application's real type.
// So this means you can get:
// var stuff:SomeStuff = Application.application.getStuff();
// but you would have to access the real application each time by saying
// Application.application. Or you can do it this way:

var app:MySubclassedApplication = mx.core.Application.application as
                                  MySubclassedApplication;

// UI components with id names are automatically declared as public
var contactList: ContactList = app.contactList;

// as long as you've declared them as public in your <Script>.:)
var dou:Dug = app.dug;
var stuff:SomeStuff = app.getStuff();

Documentation. Note when you are using SWFLoader to load a module into the main application, you can reference your main application as parentApplication.

Tracking mouse movement in Flash Player

June 8, 2009

In a recent prototyping project, I attempted to simulate “shake” gestures in a desktop AIR application, i.e., using mouse cursor to simulate a hand grabbing some object on the screen…

Anyhow. I found out that Flash Player can send out the mouseMove event must faster than it can update the screen. In the case you want to keep track of the simulated gestural motion, you would want to track the mouse cursor position on the stage, instead of the display object the mouse cursor is dragging. This is because when you drag the object to the point Flash Player cannot keep up with, the redaw will then not necessarily reflect the real positional changes of it in between the frames.

Take a look here. Right click to for the code. Also note that when directly register Application.application as the currentTarget we seem to get smoother tracking than if we registered the display object, like so:

mButton.addEventListener(MouseEvent.MOUSE_MOVE, mButton_handleMouseMove);/** you got more stripped out points than if you do this:

addEventListener(MouseEvent.MOUSE_MOVE, mButton_handleMouseMove);// this is btter.*/

MVC revisit: Take two

June 7, 2009

I find that MVC is much easier to understand both at the conceptual level and at the implementation level if you think about it from the perspective of how you communicate with the Model.

  1. Control is the input to Model. Basically, the Control is responsible to take input from the user or the system and stransfer it to the changes in the Model; The View is merely there to render the changes from the Model; so it can be considered as the output from the Model. You should keep the business logic as much as possible inside the Model and leave Control/View simple and dandy.For Flex 3 (Halo) components, the way of input is realized by implimenting the “change” listener of the controls, like this:

    <mx:TextInput id="mTxt" change="mVO.txtValue = mTxt.htmlText;"/>

    In Flex 3, when you bind a model to a control, it is one-way communication, meaning the control will reflect the change whenever there is one in the model; but the control would not be able to change the model. That’s why we need to wire through the change listener to make this happen.

    The output is hooked up by setter methods in the Model. Whenever you detect some changes that are historical, you would notify your listeners.

    [Bindable]
    public function set myValue(v:int):void
    {
    _
    myValue = v;
    notifyListeners();
    }

    In ActionScript 3, we normally implement “notifyListeners” by way of dispatchEvent. Your client listens to the event and updates the View by querying the changes from the Model. You may want to have different types of events so that your listeners can respond differently. For example, you normally would invalidate properties of your component and regenerate all the renderers when the data provider has changed, besides doing other updates; and you don’t want to do this kind of heavy-lifting each time when changes occur on the “less important” members of the Model.

  2. Asynchronous data. Sometimes you may compose something like an HTTPService component into your Model so that it can request external data.The data that are requested by the Control at a lot of times are not immediately available, and normally you would not notify your listeners until they are ready (For example, parsed into an XMLList object that can be consumed by component’s dataProvider).
  3. Static members are available before non-static members of the objects. You can use static members to set some initial values of your member variables.
  4. When you smell spaghetti code in your Model, try to partition/refactor your Model into smaller Value Objects. Value objects are a good way to structure your data/make flat Model hierarchical.
  5. For larger projects, you may want to duplicate Model (or value objects) so that multiple client can access the same data. For example, in your main window of your AIR application, you initialize your model like so:

    public var chatModel:ChatModel = new ChatModel();In another console window, you want to access the same model, like so:

    private var chatModel:ChatModel = Application.application.chatModel;

Change Adobe AIR window size dynamically

May 28, 2009

This changes your AIR app (main app’s window) to 100 px by 100 px.

Application.application.width = 100;
Application.application.height = 100;

“Unexpected end of token stream when parsing XML”

April 29, 2009

When I tried to load an external xml file using an XML tag, I got something like this from Flex Builder:

“Problem parsing external XML. Unexpected end of token stream. The last token was: 360.”

The file was built in Nodepad++ and was displayed fine in Firefox as well as in Eclipse/FB. When I got the error message, I then tried to load it in IE, which also gave me an error. What I did to fix it was going back to Nodepad++ and  “Format>Encode in UTF-8“, which basically changed the encode from ANSI (the default one for Notepad++) to Unicode. I got several “special” unicode characters such as 360° in my orignial XML file.


Follow

Get every new post delivered to your Inbox.