Symmetri Developer Blog

November 4, 2009

Using a Flash symbol from an SWF as the background for a UIComponent using styles

Flash/Flex - By Shourov Bhattacharya

You can use a Flash movie clip from within an SWF as the background for a component in Flex. First, you must create the symbol in Flash and then be sure to create a linkage for the symbol (Export for Actionscript etc.) and give it a unique name; then publish the Flash movie as a SWF. Within Flex, the background can be set up as part of a style in the CSS stylesheet - but the part that had me stumped was that instead of using background-skin, you must use background-image. That is, the following code is correct and will work:

.bubble
{
    background-image: Embed(source=../swf/bubble_skins.swf, symbol=bubble);
    background-alpha: 1.0;
    background-size: 100%;
}

The following code will NOT work:

.bubble
{
    background-skin: Embed(source=../swf/bubble_skins.swf, symbol=bubble);
    background-alpha: 1.0;
    background-size: 100%;
}

Dividing the perimeter of an ellipse into equal segments

Flash/Flex, Algorithms - By Shourov Bhattacharya

Suppose we want to lay out N number of objects on the perimeter of a circle, equally spaced. Finding the position of each object is trivial; we simply divide the total angle 2*PI by N and then draw out a point at the constant radius at that angle. However, doing the same thing on an ellipse is surprisingly tricky. Using the same algorithm as the circle in polar form does not yield the right results; the points end up “bunching up” on the minor axis. To find the correct algorithm, we need to think parametrically - we parametrize the elliptical curve by a variable theta and then plot points at equadistant intervals projected on the linear axis of theta:

var theta:Number = (Math.PI*2)*(i/NUMBER_OF_SEGMENTS) - Math.PI/2;
var a:Number = 100;
var b:Number = 80;
var x:Number = a* Math.sin(theta);
var y:Number = b*Math.cos(theta);
var point:Point = new Point(x, y);

October 13, 2009

Duplicate event listeners in Flex?

General, Flash/Flex - By Shourov Bhattacharya

Does adding the same event listener multiple times to a Flex object cause multiple, duplicate postbacks? I didn’t know for sure, but I just tried it and the answer is (thankfully) no. Let the good times carry on, then.

October 5, 2009

Adding same child to Container twice gives error “Error #2006: The supplied index is out of bounds.”

Flash/Flex - By Shourov Bhattacharya

I thought I would document this as I think the error message is rather unhelpful and even misleading. In Flex, say you add the same child DisplayObject to a Container:

// container is of type Container
// child is of type DisplayObject
container.addChild(child);
container.addChild(child);

you get the error message “Error #2006: The supplied index is out of bounds.” That’s not a particularly helpful message, because it’s a very general error that is generated inside the Container.addChildAt() method; even though you are not explicitly defining a child index, the error is related to indexing of the array that holds children of the container. You would get the same error if you tried to use container.addChildAt() and specified an invalid index.

A better error message for this case would be something like “Duplicate child object not allowed in single instance of Container”.

October 1, 2009

Flex CSS property font-family is case-sensitive!

General, Flash/Flex - By Shourov Bhattacharya

This is just a little thing, but I have noticed that it isn’t widely understood. When using CSS in Flex to style components, the font-family property can be case sensitive on some operating systems. For example, the following CSS will not work on my Safari browser with Flash Player 10:

.content{font-family: arial;}

but this will work:

.content{font-family: Arial;}

Flex Layout with draggable, resizable components

Flash/Flex - By Shourov Bhattacharya

I have been looking for a way to layout elements (child components) within a Flex container that is flexible, resizable, draggable and visually impressive. The main requirements are that a) the individual child elements can be reordered with drag-and-drop, b) elements can be maximized/minimized, c) elements can expand to fill the available space and d) elements can be given a default width and height.

Unfortunately, none of the native Flex components have layout algorithms that are suitable. They mostly implement absolute positioning or a simple vertical or horizontal “flow” layout which can fit elements into the parent container, but without any resizing.

Recently I have found a really nice implementation of layout within Flex called the Adobe Flex 3 Dashboard sample. It implements a “pod layout” which handles all my requirements EXCEPT for the fact that the individual elements (”pods”) cannot be given an initial/default width and height - they simply scale to each take up an equal amount of space within the parent container. The drag and drop functionality also has some nice animated effects.

September 16, 2009

Flex AdvancedDataGrid issues and code quality

General, Flash/Flex - By Shourov Bhattacharya

There is a fair bit of chatter over at Adobe’s Flex blog about the Data Visualization Components and the AdvancedDataGrid in particular. Some developers have complained to Adobe about the number of bugs in AdvancedDataGrid and the slow pace of bug fixing; many have said that the ADG is basically unsuitable for production.

Here is another popular post that makes the point that the ADG (and the Flex framework codebase in general) is ill-designed and bloated.

I’ve just completed a commercial project which used the AdvancedDataGrid and Charting components from the Data Visualization libraries, and would like to share my view. Generally speaking, I felt that the experience was a positive one. There were bugs that I encountered and had to work around (such as this one and this one). I did have my share of WTF moments. But in the end I was able to get a complex visualization product out the door on schedule without hitting any real roadblocks. Some workarounds required creativity, but I never found myself stuck for a solution; I was always able to overload a method, extend a class or find an alternative way to get things working. Given the specifications for the project - delivering rich, interactive reports through the browser for a variety of client OSes and platforms - and the very tight timelines, I have to say that I was happy in the end that I chose to use Flex.

Here’s another point I want to make. Adobe has open-sourced the code for the Flex framework, and that is a positive development. Any negative comment about Flex framework code quality is offset by the benefit of transparency - for a .NET developer, the fact that we can read the framework code at all is a novelty. The big picture here is that we have a commercially developed codebase that is being poked and prodded to death by the developer community. Many of the concerns about code quality are valid, but I think Adobe has done the smart thing by throwing its baby to the wolves, as it were. In the long term, the Flex framework will be improved, and the engagement with the developer community - albeit negative in parts - will be a genuine differentiation between their RIA platform and the Microsoft competition*.

Also, I think the developer community as whole should has a collective personality that is hard to please. We software developers love to take other people’s code and tear it apart. What we see are the logical and design flaws, the shortcuts, the omissions and the lack of documentation. What we don’t see is the context within which the code was written in the first place - the deadlines, the commercial pressures, the collaborative environment, office politics and the whole zero-sum game of allocating finite resources between new development and support. That stuff can be mitigated but it can’t be eliminated. In the real world, code is never written in a vacuum, and no one has the luxury of getting their code exactly right.

Of course the Flex framework has flaws, and Adobe has a responsibility to the development community to listen to feedback and address issues in good faith. If they fail to do that, they deserve the bad reputation that would follow. But the real test of software is whether it gets the job done, not whether it violates the latest popular design principles or whether it conforms to one’s personal taste. By that yardstick, I give Flex a (qualified) thumbs-up based on the experience of taking two-and-a-half real-world Flex projects through to production.

One final point. I have done RIA projects in both Flex and Silverlight/WPF over the last year. Each experience has had its share of ups and downs, but neither has come out clearly better for me. As developers, we can’t write our own frameworks and SDKs - we have to work with the available alternatives. The Flex SDK may be flawed in absolute terms, but what is more important is its relative quality as compared to the competition. And that we can’t compare right now, because the .NET framework is a black box. I wonder, after Adobe’s move, would Microsoft consider making it transparent?

*UPDATE: Microsoft is indeed planning to release the codebase for the .NET Framework libraries. That’s great news, and an important step forward for the whole concept of open-source. It will generate a lot of goodwill for Microsoft.

June 23, 2009

Nulls in (Advanced)DataGrid column causes a column sort to throw RTE

Flash/Flex - By Shourov Bhattacharya

If an (Advanced)DataGrid has null values in the data provider, then sorting the associated column in the grid throws the following error:

Error: Cannot determine comparator for SortField with name ''XXX''.

Adobe already has a bug report: https://bugs.adobe.com/jira/browse/SDK-13808?actionOrder=desc.

There is a bit of discussion around this, with some people of the opinion that this is not really a bug - that it is the developer’s responsibility to ensure that either a) there are no nulls in the data or b) if there are, write your own sort compare function that handles the nulls. I call it a genuine bug in the Flex SDK; if my grid displays nulls, then it should know how to sort nulls as well - i.e. don’t tell me my data is valid when displaying but then not valid when sorting. Especially as there is an intuitive rule-of-thumb that could be used for sorting nulls of any data type (send them to the top of the ascending sort).

Probably the best workaround is to avoid the nulls in the first place. In my case, I couldn’t do that in the data source, so I did it in the item renderer:

package com.symmetri.skins
{
    import mx.controls.advancedDataGridClasses.*;
	
    public class ReportDataGridItemRenderer extends AdvancedDataGridItemRenderer
    {
        public function ReportDataGridItemRenderer()
        {
            super();
        }
	
         override public function validateNow():void
         {
             if ((listData) && (data))
             {
                  var _fieldName:String = AdvancedDataGridListData(listData).dataField;
                   var _value:String = String(data[AdvancedDataGridListData(listData).dataField]);
	
            //fill out null values with blanks
            if (data[AdvancedDataGridListData(listData).dataField] == null) data[AdvancedDataGridListData(listData).dataField] = ;
             }
	
             super.validateNow();
         }        
	
    }
}

Actually, I just though of something: does this approach have a shortcoming - will it work if not all rows in the data are rendered?

June 22, 2009

Flex bug when rendering AdvancedDataGrid with a small height

Flash/Flex - By Shourov Bhattacharya

I couldn’t find an Adobe bug report for this, so I might go ahead and log one. I have a number of AdvancedDataGrid components in my interface, and I have encountered a run-time error when resizing the interface. It appears to happen whenever one of the AdvancedDataGrid components is resized down to a very small size - less than about 40 pixels height. The error message is:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mx.controls::AdvancedDataGridBaseEx/drawColumnBackground()
[C:\work\flex\dmv_automation\projects\datavisualisation\src \mx\controls\AdvancedDataGridBaseEx.as:3483]

For a workaround, I first tried to capture the ResizeEvent.RESIZE event on the AdvancedDataGrid and then test for the height of the grid; I set visible=false and includeInLayout = false whenever the grid was too small:

public function checkGridOnResize(evt:ResizeEvent):void
        {
            var grid:AdvancedDataGrid = AdvancedDataGrid(evt.currentTarget);
	
            grid.visible = (grid.height < 50 ? true: false);
            grid.includeInLayout = grid.visible;
        }

However, I was surprised to see that this didn’t work. The RTE was still being thrown, and the AdvancedDataGridBaseEx.drawColumnBackground() method was still being called, even though the grid was invisible. So I had to extend the AdvancedDataGrid class and catch the error in an overload of the drawColumnBackground method:

package com.symmetri.skins
{
    import mx.controls.AdvancedDataGrid;
    import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
    import mx.core.UIComponent;
	
    public class ReportDataGrid extends AdvancedDataGrid
    {
	
        public function ReportDataGrid()
        {
            super();
        }
	
        override protected function drawColumnBackground(s:Sprite, columnIndex:int, color:uint, column:AdvancedDataGridColumn):void
        {
            // this is done to handle a Flex bug
            // RTE thrown when rendering a grid smaller than about 40 pixels in height
            try
            {
                super.drawColumnBackground(s,columnIndex,color,column);
            }
            catch(e:Error) {}
        }
    }
}

Not the ideal solution, but the best we can do at this point. When (if) I get a spare moment, I will post the bug report for Adobe.

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>

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