Symmetri Developer Blog

May 25, 2009

Open all branches in a Flex tree

Flash/Flex - By Shourov Bhattacharya

I need to create a Tree in Flex which starts off with all the nodes opened. This proves to be much harder than you might think. The solution I have found relies on a bit of hacking to keep track of when the Tree's data provider has been loaded, and then “flattening” the tree data structure into an ArrayCollection and using that as the openItems property of the Tree. In my case I am using an ArrayCollection of Objects as the data source.

A bonus is the ability to open the Tree to a particular depth. This comes in handy to stop a very deep tree from running away with your scripting.

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml>
	
<mx:Script>
    <![CDATA[
	
import flash.events.*;
import mx.collections.ArrayCollection;
import mx.controls.*;
import mx.core.*;
	
public var categoryTreeOpened:Boolean = false;
public var categories:ArrayCollection;
	
// create categories tree data structure as nested ArrayCollections of objects here
	
// label function for components which read from objects
protected function objectLabel(item:Object):String
{
     return item.name;
}
	
// open the category tree first time it is rendered
public function openAllCategories(event:Event = null):void
{
    if ((categoryTreeOpened == false) && (this.categories != null))
    {
        var openItems:ArrayCollection = this.flattenCategoryTree(this.categories, new ArrayCollection(), 4);
        this.trCategories.openItems = openItems;
        categoryTreeOpened = true;
    }
}
	
// flatten the category tree to a certain level
protected function flattenCategoryTree(_categories:ArrayCollection, input:ArrayCollection, level:Number = 5):ArrayCollection
{
    if (level == 0) return new ArrayCollection();
	
    for each (var _category:Object in _categories)
    {
        input.addItem(_category);
	
        input = new ArrayCollection(input.toArray().concat(flattenCategoryTree(_category.children, input, level-1).toArray()));
    }
	
    return input;
	
}
    ]]>
</mx:Script>
	
<mx:Tree  dataProvider={categories} labelFunction=objectLabel id=trCategories render=openAllCategories()/>
	
</mx:Application>

May 22, 2009

Render a bar graph within a Flex (Advanced)DataGrid

Flash/Flex - By Shourov Bhattacharya

A nice visual representation of a variable such as a “percentage growth” is a bar graph that shows the value around a central zero point, with different colour bars for negative and positive growth. To render this in an AdvancedDataGrid, we need to create a custom itemRenderer class that overloads the updateDisplayList method and draws a bar using the UIComponent.graphics object. This will give you a grid which looks something like this:

renderBar

Full source code for the item renderer class below.

package com.symmetri.utils.skins
{
    import flash.display.Graphics;
	
    import mx.controls.advancedDataGridClasses.*;
    import mx.controls.listClasses.*;
    import mx.core.IDataRenderer;
    import mx.core.UIComponent;
    import mx.events.FlexEvent;
    import mx.utils.GraphicsUtil;    
	
    [Event(name=dataChange, type=mx.events.FlexEvent)]
    public class ReportBarItemRenderer extends UIComponent implements IDataRenderer, IDropInListItemRenderer, IListItemRenderer
    {
        private var _data : Object = null;
        private var _listData : BaseListData = null;    
	
        public var BAR_MAX_WIDTH:Number = 17;
        public var BAR_HEIGHT:Number = 13;
        public var BAR_X:Number = 20;
        public var BAR_Y:Number = 1;
	
        public var DATA_MAX_VALUE:Number = 50;
	
        public var COLOR_POSITIVE:Number = 0x0D84A4;
        public var COLOR_NEGATIVE:Number = 0xD00A3D;        
	
        public function ReportBarItemRenderer()
        {
            super();
        }
	
        public function get data():Object
        {
            return _data;
        }
	
        public function set data(value:Object):void
        {
            this._data = value;
            this.invalidateProperties();
	
            dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
        }        
	
        public function get listData():BaseListData
        {
            return _listData;
        }
	
        public function set listData(value:BaseListData):void
        {
            this._listData = value;
            this.invalidateProperties();
	
            dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
        }    
	
         override protected function updateDisplayList(w:Number, h:Number):void
         {
             super.updateDisplayList(w, h);
	
             if (_data != null)
             {
                // draw a horizontal bar to represent value of data
                // scaled between -1.0 and +1.0
                var _fieldName:String = AdvancedDataGridListData(listData).dataField;
                var _value:String = String(data[AdvancedDataGridListData(listData).dataField]);                            
	
                if (isNaN(Number(_value)))
                {
                    // do nothing, data value not numeric
                }
                else
                {
                    // draw the bar
                    var _scaledValue:Number = Number(_value)/DATA_MAX_VALUE;
                    var _width:Number = _scaledValue*BAR_MAX_WIDTH; 
	
                    // keep a minimum width of 1 pixel
                    if ((_width > -1) && (_width < 0)) _width = -1;
                    if ((_width < 1) && (_width > 0)) _width = 1;
	
                      graphics.clear();
                    graphics.lineStyle(1, 0xFFFFFF, 0); // no border
	
                    if (_scaledValue < 0)
                    {
                        // draw a negative bar
                        graphics.beginFill(COLOR_NEGATIVE, 1);
                        GraphicsUtil.drawRoundRectComplex(graphics, BAR_X+_width, BAR_Y, -_width, BAR_HEIGHT, 0, 0, 0, 0);
                    }
                    else
                    {
                        // draw a negative bar
                        graphics.beginFill(COLOR_POSITIVE, 1);
                        GraphicsUtil.drawRoundRectComplex(graphics, BAR_X, BAR_Y, _width, BAR_HEIGHT, 0, 0, 0, 0);
	
                    }
                     graphics.endFill();
	
                     // draw a tooltip with the data value
                     this.toolTip = _value.toString();
                }
             }
	
             super.validateNow();
         }
    }
}

May 20, 2009

Flex compiler shows some smarts

General, Flash/Flex, .NET/C# - By Shourov Bhattacharya

Developers like to compare IDEs. I use both Adobe Flex Builder 3 and Microsoft Visual Studio regularly. I think both are pretty good; each has its quirks and problems, perhaps Visual Studio marginally more than Flex Builder. One little thing I noticed while developing my Flex project today; Flex Builder compiler generates a warning when a single equal sign (=) is used in place of a double (==) for a equality comparison within a conditional statement. For example, the following statement:

if (x = y) {// do something }

will generate an compiler warning “Assignment within conditional statement (did you mean == instead of =?)”. I appreciated that. This is one of the most common syntax errors for programming newbies, and even us old hands fall prey to it on those late nights when the code is being churned out on autopilot. I think VS2008 would let this statement through the compile without batting an eyelid. Are there any other IDEs which pick up this very common syntax error?

May 14, 2009

Repeating image background in Flex

Flash/Flex - By Shourov Bhattacharya

Repeating an image in the background of a visual component in Flex turns out to be non-standard and not well documented. Given Flex 3 excellent support for CSS, you would probably assume that using background-image and background-repeat would do the trick. However, it turns out that background-repeat is not supported. The way to do it is to use the background-size style and set it to 100%:

.navPrimary
{
    background-image: Embed(../images/nav-background.png);
    background-size:100%;
}

May 11, 2009

ArrayCollection refresh bug

Flash/Flex - By Shourov Bhattacharya

This is a funky bug. I have an ArrayCollection as the dataProvider for a ColumnChart; when I change the data values within the ArrayCollection, the chart fails to update to display the new values. The workaround is easy enough: call ArrayCollection.refresh();. It’s a pretty basic bug though; Adobe really should get it fixed.

See https://bugs.adobe.com/jira/browse/SDK-13247

View MHTML in Flex?

General, Flash/Flex, .NET/C# - By Shourov Bhattacharya

Everything you need for a webpage in one file, including external links to images, Flash files etc? Sounds like a good idea I guess; it would mean that you could easily email a single webpage to someone for offline viewing, for example. This was, I presume, the idea behind the MHTML format, which MIME-encodes a HTML webpage and associated resources into one long binary file. However, the problem is that very few browsers support the display of MHTML files natively. Internet Explorer does, but not Firefox, Safari or Google Chrome.

In my particular case, I have a bunch of reports generated by a third-party which are saved as MHTML files which need to be displayed within a Flex iFrame component (which is basically a floating browser container). I could display directly in the browser frame, in which case only users on IE will see the display. I could get the client to use a MHTML to HTML converter software. Or I could write my own server-side code to do the conversion on-the-fly, maybe using this code as a starting point.

Get free blog up and running in minutes with Blogsome
Theme designed by Janis Joseph