by Peter Ent

Peter Ent

Table of contents


24 November 2008


Prerequisite knowledge

To benefit most from this article, it is best if you are familiar with Flex Builder and ActionScript 3.0.


User level


Required products

Flex Builder 3 (Download trial)

If you are displaying a large number of itemRenderers — either in the DataGrid or AdvancedDataGrid — your application's performance may be adversely affected if you do not code these itemRenderers efficiently. Here are some tips that might help:
  • Limit the number of columns using itemRenderers. Do you really need to have every column be a custom itemRenderer? Sometimes you do, but is all that glitz overwhelming the user?
  • Try not to change the style of the elements in your itemRenderer too frequenty. If you need to switch styles (for instance, green for positive values, red for negative values), consider having two controls preset with those styles and making one visible. Changing styles is one of the more time-consuming tasks in Flex.
  • Do not use Containers as the basis for your itemRenderers. Containers have a lot of overhead. They are fine for limited use, but it would be more efficient to write your itemRenderers based on UIComponent.
This series includes the following articles:

Switching styles

Here's an itemRenderer which switches components depending on the value of the data field.
<mx:Canvas> <mx:Script><![CDATA private function lessThanZero() : Boolean { return data.price < 0; } ]]></mx:Script> <mx:Label text="{data.price}" color="#FF0000" visible="{lessThanZero()}" /> <mx:Label text="{data.price}" color="#00FF00" visible="{!lessThanZero()}" /> </mx:Canvas>
This will be faster than setting the style. Some other things to keep in mind:
  • Avoid data-binding to styles. Not only is changing styles slower than most operations, adding data-binding code on top of it just makes it worse.
  • Use a Canvas or extend ListItemRenderer or as the root of the itemRenderer. This enables you to place controls on top of each other.

Extending UIComponent

By far the most efficient way to write an itemRenderer is to extend UIComponent using an ActionScript class. You'll have complete control of the code, and the renderer will be as efficient as possible.
Start with the example above, switching styles, and write a simple itemRenderer extending UIComponent.
package renderers { import mx.controls.listClasses.IListItemRenderer; import mx.core.UIComponent; public class PriceItemRenderer extends UIComponent implements IListItemRenderer { public function PriceItemRenderer() { super(); } } }
You'll notice that not only did I write the class to extend UIComponent, I also have it implementing the IListItemRenderer interface. It is necessary to do this because a List control will expect any renderer to implement this interface, and if you do not, you'll get a runtime error as the list attempts to cast the renderer to this interface.
If you read the documentation on IListItemRenderer, you'll see that is an amalgamation of many other interfaces, most of which UIComponent implements for you. But there is one interface extended by IListItemRenderer that UIComponent does not implement: IDataRenderer. This requires you to add the code to give the itemRenderer class the data property you've been using all along.
If you attempt to use this class without implementing IDataRenderer, you'll get these errors when you compile the code:
  • 1044: Interface method get data in namespace mx.core:IDataRenderer not implemented by class renderers:PriceItemRenderer.
Edit this class and change it to the following:
package renderers { import mx.controls.listClasses.IListItemRenderer; import mx.core.UIComponent; import; public class PriceItemRenderer extends UIComponent implements IListItemRenderer { public function PriceItemRenderer() { super(); } // Internal variable for the property value. private var _data:Object; // Make the data property bindable. [Bindable("dataChange")] // Define the getter method. public function get data():Object { return _data; } // Define the setter method, and dispatch an event when the property // changes to support data binding. public function set data(value:Object):void { _data = value; dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); } } }
I took the code directly from the Flex documentation for IDataRenderer, so you don't even have to type it yourself.
With that out of the way, you can add in the two labels.
  1. Add variables to hold the two labels. private var posLabel:Label; private var negLabel:Label;
  2. Modify the set data function to call invalidateProperties(). This is important, because the change of the data has to change the text in the labels AND their visibility.
public function set data(value:Object):void { _data = value; invalidateProperties(); dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); }
Calling invalidateProperties() tells the Flex framework to call the commitProperties() function at the apppriate time.
  1. Override createChildren() and create the labels, adding them to the display list of the component. Notice that in addition to creating the labels, their styles and visible properties are also set.
override protected function createChildren() : void { super.createChildren(); posLabel = new Label(); posLabel.visible = false; posLabel.setStyle("color", 0×00FF00); addChild(posLabel); negLabel = new Label(); negLabel.visible = false; negLabel.setStyle("color", 0xFF0000); addChild(negLabel); }
  1. Override commitProperties() to set the labels' text and visibility. In the past you've been overriding set data to make this type of change, and you can do that in this class, too, if you prefer.
override protected function commitProperties():void { super.commitProperties(); posLabel.text = data.price; negLabel.text = data.price; posLabel.visible = Number(data.price) > 0; negLabel.visible = Number(data.price) < 0; }
  1. Override updateDisplayList() to size and position the labels. You must size the labels, because their default size is 0×0. This is another thing a Container class will do for you. Since this is a pretty simple itemRenderer, you can just set the labels' size to match the size of the itemRenderer.
override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ) : void { super.updateDisplayList(unscaledWidth, unscaledHeight); posLabel.move(0,0); posLabel.setActualSize(unscaledWidth,unscaledHeight); negLabel.move(0,0); negLabel.setActualSize(unscaledWidth, unscaledHeight); }
All this probably seems a bit complicated just to do this, but keep in mind that using a container will add a lot more code than this.

UIComponent notes

The UIComponent class is the basis for all visual Flex components—controls and containers. Here are some tips about using UIComponent as your itemRenderer.
  • UIComponent imposes no layout restrictions on its children (unlike a Container). You have to position and size the children yourself.
  • It is also possible to draw graphics and position children beyond the size specified in updateDisplayList().
  • If you plan on using variableRowHeight in your list, you should also override the measure() function to give the list an idea of how big the itemRenderer is.
  • To use UIComponent as an itemRenderer you must implement IDataRenderer.
  • To use the listData property you must implement IDropInListItemRenderer; that was covered in a previous article of this series.

Where to go from here

This concludes my series on Flex itemRenderers. I hope these articles have given you a sense what you can accomplish in your Flex applications using itemRenderers, as well as best practices for how to manipulate their data, appearance and behavior. You can find more information about Flex itemRenderers in the Adobe Flex 3 documentation.