Google analytics code

Monday, November 30, 2009

Flex (Learning Flex 3 Book) : Removing IEventDispatcher error from the DataGrid

When I decided to learn Flex I started with Learning Flex 3. It's a great book and does an excellent job of introducing the MXML framework and Flex Builder IDE. I would recommend it to anyone.

One of the problems I have with the book is it doesn't teach you how to remove warnings from your code. A later code example produces the following error: warning: unable to bind to property 'contact' on class 'XML' (class is not an IEventDispatcher). It's not a big deal, but I like to write code that doesn't produce hidden errors.

Here is the code from the book:
<mx: Application
    xmlns: mx="http: //www.adobe. com/2006/mxml"
    xmlns: view="com.oreilly.view.*"
    layout="absolute"
    applicationComplete="contactsService.send()" >
    <mx:HTTPService id="contactsService"
        resultFormat="e4x"
        url="contacts.xml" />
    <mx:DataGrid id="contactsDataGrid"
        dataProvider="{contactsService.lastResult.contact}"
        selectedIndex="0"
        left="10"
        top="10"
        bottom="10"
        width="300">
        <mx: columns>
            <mx: DataGridColumn headerText="First"  
            dataField="firstName"/>
            <mx: DataGridColumn headerText="Last" dataField="lastName"/>
        </mx:columns>
    </mx:DataGrid>
    <view: ContactViewer
        contact="{contactsDataGrid. selectedItem}"
        x="318" y="10">
    </view: ContactViewer>
</mx: Application>

The problem is the dataProvider property of DataGrid. It's unable to figure out how to parse the data, so it throws a warning. This can be resolved with casting. We can tell the dataProvider what it should be and it will remove the warning. Here is the code that will remove the warning.

dataProvider="{XMLList(XML(contactsService.lastResult).contact)}"

contactsService.lastResult is an XML object. There are multiple children in the object. We can then cast the XML object as an XMLList to use contact child node.

Feel free to leave a comment if you have any questions.

Wednesday, November 18, 2009

Flex: TileList cell switching/redraw problem using an itemRenderer

While working with the TileList component I ran into a very odd issue. Images would move around while scrolling up and down. Also when I updated the dataProvider the TileList didn't display the new information. Here is an example so you can see what is going on. You can view the source the source by right-clicking and selecting "View Source".

After a lot of research, and a post on StackOverflow, I figured out what was going on. My broken example relies on a MXML completion method (init, creationComplete, etc..)  to set the source of the image in the itemRenderer.

index.mxml
<mx:Component>
    <itemRenderers:ImageTile_bad />
</mx:Component>

ImageTile_bad.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Image xmlns:mx="http://www.adobe.com/2006/mxml" initialize="init()">
    <mx:Script>
        <![CDATA[
            /**
            * This is an example of how not to use an item render in a TileList  
            * */
            private function init() : void {
                this.source = data;
                this.name = data.toString().split("/").pop().split(".").shift();
            }
        ]]>
    </mx:Script>
</mx:Image>

TileList reuses cells inside the component. I'm not sure why, but can't keep track of where things should render and starts switching images around. I tried overridding the data method and it works, but I wanted to find a solution that doesn't require overriding a private method.

The answer is to use a setter inside the itemRenderer to assign the value to the component. Here is a snip-it of code to show you what I mean.

index.mxml
<mx:Component>
    <itemRenderers:ImageTile img="{data}"/>
</mx:Component>

ImageTile.mxml
<mx:Image xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            /**
            * This is an example of how to use an itemRenderer in a TileList
            * Create a setter that will always update the when the TileList redraws
            * */
            public function set img(value:String) : void {
                //make sure there is something to work with. avoid error# 1010
                if(!value) {
                    return;
                }
                this.source = value;
                this.name = value.toString().split("/").pop().split(".").shift();
            }
        ]]>
    </mx:Script>
</mx:Image>

When TileList redraws the cell it will pass data into the img setter. This will make sure that the cell receives the correct information when it redraws. Also switching the dataProvider on the TileList works properly now. Here is a link to the working example that has available source code. Feel free to contact me if you have any questions.

Thursday, November 12, 2009

Flash / ActionScript 3: Event Bubbling Example

Previously I wrote an article that showed an example of Event Bubbling with Flex. I wanted to write a similar example for ActionScript 3. The principle is the same, but it works just a little different.

Any class involved in bubbling must extend DisplayObjectContainer. Sprite is the most basic DisplayObjectContainer class and most examples I see use it, so that's what I will use. I gave a brief overview of how event bubbling works in the Flex example that I won't repeat here.

I'm creating three classes that include each other. The primary class includes Level1. Level1 includes Level2. Level2 includes Level3. Level3 triggers an event when it's added to the stage. The event from Level3 will bubble through Level2 and Level1 to the primary class without re-dispatching it.

event_bubbling_as.as
package {
    import flash.display.Sprite;
    import flash.events.Event;
    
    import obj.Level1;
    import obj.Level3;

    public class event_bubbling_as extends Sprite
    {
        private var _level1:Level1
        
        public function event_bubbling_as()
        {
            _level1 = new Level1;
            _level1.addEventListener(Level3.EVENT, onEvent);
            
            addChild(_level1);
        }
        
        private function onEvent(event:Event) : void {
                trace('caught event from level 3');
        }
    }
}

Level1.as
package obj
{
    import flash.display.Sprite;
    
    public class Level1 extends Sprite
    {
        private var _level2:Level2;
        
        public function Level1()
        {
            trace("in level 1");
            _level2 = new Level2;
            
            addChild(_level2);
        }
    }
}
Level2.as
package obj
{
    import flash.display.Sprite;
    
    public class Level2 extends Sprite
    {
        private var _level3:Level3;
        
        public function Level2()
        {
            trace("in level 2");
            _level3 = new Level3;
            
            addChild(_level3);
        }
    }
}
Level3.as
package obj
{
    import flash.display.Sprite;
    import flash.events.Event;
    
    public class Level3 extends Sprite
    {
        public static const EVENT:String = "level3";
        public function Level3(){
            trace("in level 3");
            
            //trigger event when the object is added to the stage
            addEventListener(Event.ADDED_TO_STAGE, launchEvent);
        }
        
        public function launchEvent(event:Event) : void {
         var newEvent:Event = new Event(EVENT, true, true);
         dispatchEvent(newEvent);
        }
    }
}
You can download the Flex Project Archive example here. This can be directly imported into Flex. Run it in debug mode to see how each level is triggered.

Sunday, November 1, 2009

Flex: Event Bubbling Example

A refactor it gave me a chance to update the way I handled events. This page on Adobe's site explains the event bubbling model. To give a brief overview there are three phases: Capture, Targeting and Bubbling. The Capture phase will pass through each branch of a DisplayObject tree until it reaches the last node. The Targeting phase will look for objects that have event listeners bound to them. The Bubbling phase ascends the DisplayObject tree from the last node to the first node and activates the event listeners. Only DisplayObjects have a Capture and Bubbling phase.

Here is some example code I made in MXML that shows how event bubbling works. There are four files. The Application file includes Layer1. Layer1 includes Layer2. Layer2 includes Layer3. When Layer3 is created it will throw an event. The first property of the event is the string that will trigger event listeners, the second allows the event to bubble through the DisplayObject tree. The third allows the event to be canceled at any of the DisplayObjects it passes through.  The event will bubble through each DisplayObject and back to the Application without manually passing it forward.

UPDATE
If you're looking for an example of event bubbling in ActionScript please view this article.

index.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()" styleName="plain">
    <mx:Script>
        <![CDATA[
            import obj.Level3;
            import obj.Level1;
            
            private var _level1:Level1;
            
            private function init() : void {
                trace("in the base");
                _level1 = new Level1;
                
                //add eventListener
                _level1.addEventListener(Level3.EVENT,onEvent);
                
                addChild(_level1);
            }
            
            private function onEvent(event:Event) : void {
                trace("picked up event from Level3 ");
            }
        ]]>
    </mx:Script>
</mx:Application>


Layer1.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" initialize="init()">
    <mx:Script>
        <![CDATA[
            private var _level2:Level2;
            private function init() : void {
                trace("in level 1");
                _level2 = new Level2;
                addChild(_level2);
            }
        ]]>
    </mx:Script>
</mx:Canvas>

Layer2.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" initialize="init()">
    <mx:Script>
        <![CDATA[
            private var _level3:Level3;
            private function init() : void {
                trace("in level 2");
                _level3 = new Level3;
                addChild(_level3);
            }
        ]]>
    </mx:Script>
</mx:Canvas>

Layer3.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" initialize="init()">
    <mx:Script>
        <![CDATA[
            public static const EVENT:String = "level3";
            private function init(): void {
                trace("in level 3");
                var event:Event = new Event(EVENT,true,true);
                dispatchEvent(event);
            }
        ]]>
    </mx:Script>
</mx:Canvas>

You can download the Flex Project Archive example here. This can be directly imported into Flex. Run it in debug mode to see how each level is triggered.