Flex 2 DataGrid ItemRenderers are re-used! Warning!!!

After battling with Flex 2’s DataGrid ItemRenderer for a few hours, I have finally figured out how they really work.

The Scenario

Let’s say you want to have a DataGrid with a thumbnail in one of the cells. To do this you would assign the DataGridColumn an ItemRenderer. Here’s a quick and dirty MXML ItemRenderer that will work:

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" verticalScrollPolicy="off" horizontalScrollPolicy="off">
  <mx:Image id="thumbnail" width="25" height="25"/>
</HBox>

Now, when the DataGrid goes to render the cell the first time, it instantiates the ItemRenderer and sets the data property to the data of the current row in the DataGrid. Let’s say you have a property called thumb_src in the data. You could just bind the image’s source property to data.thumb_src and you probably wouldn’t have any problems, but if you wanted to do anything to the data before you use it you will run into trouble. The problem is that DataGrids only create as many ItemRenderers as are visible in the grid, so if you have 1000 rows in your DataGrid but you can only see 10, only 10 ItemRenderers will be created (more will be created on demand if you resize the DataGrid). Once a cell is scrolled off the top of the grid it is reused at the bottom (and visa-versa). When the ItemRenderer is reused, the DataGrid sets the data property with the new data and that’s it - it is not destroyed re-instantiated.

The Problem

All this becomes a major problem when you use the creationComplete event to setup some stuff on your renderer, since this event is only fired once - then renderer will be used and re-used to display the data from many different cells. When this problem occurs, you will see data in the ItemRenderer cells being thrown around seemingly at random when you scroll the DataGrid - it will probably drive you crazy!

The Solution

You need to setup / clear everything in the renderer when the data property is set. The easiest way to do this is to override the Container’s data setter function like this:

public override function set data(value:Object):void{
  // This will fire off the FlexEvent.DATA_CHANGE Event
  super.data = value;
  // if the value is null this cell is empty
  if(value == null){
    // clear all the controls
    return;
  }
  // set the controls with this data
}

You could also add an event listener for FlexEvent.DATA_CHANGE, which is fired off by the Container superclass.

Here’s a great example that I made that shows the problem. You can right click on the application and hit “View Source” to see the code, or go there directly.

stevekamerman

COO @scientiamobile