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:
<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:
// 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.
« My desk gets another monitor :D Deleting tons of files in Linux (Argument list too long) »


Thanks a lot for this "debugging", we had so many troubles with that problem without understanding it. It's working fine now
except that it's a little laggy now.. I hope it will be corrected with the release version!
Cheers!
Thanks for the post, really helps show what is going on. But what if you have ComboBoxes as itemRenderes? How would you make sure they are in the right location with the correct selectedIndex?
Hi Tim – just make sure you set your comboboxes in the set data function:
[code]
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
[b]myComboBox.selected = false;[/b]
return;
}
// set the controls with this data
[b]myComboBox.selected = data.myComboBoxSouce;[/b]
}
[/code]
Many thanks! This was driving me crazzzzyy and nothing in the docs seemed to help / point me in the right direction, till I found you comments. I kept thinking somehow it was the passed in data.
-Bill
Hi,
I wanted to thank you for your blog entry because it has helped me tremendously. I now have a slightly different problem though and I cannot, for the life of me, figure out what is going on. I believe it is a similar problem that this entry resolves.
I have an <AdvancedDataGrid> that is grouped, meaning that the dataprovider is a GroupingCollection but it gets more complicated. In this Advanced Data Grid, I am using a <AdvancedDataGridRendererProvider> which renders a component called "thumbNailRenderer".
The reason behind this is that I needed to display a listing of Departments that are grouped and in each of these departments I need to display a row of thumbnail images of the employees.
I actually have all this working, but my problem is that in the "thumbNailRenderer" component, there is an <mx:Image source="{data.ntlogon}.jpg"/> call. In my style sheet, i'm doing a brokenImageSkin:Embed("nophotoman.gif") on this <mx:Image> node. So whenever there the source jpg cannot be found, I wanted to show a generic no_photo.jpg image instead of the broken image icon that shows by default whenever an image cannot be found. I know my path to the "nophotoman.gif" image is correct but the image does not show.
I've also tried using the <mx:Image ioError=""/> call but when I go this route the AdvancedDataGrid starts refreshing like crazy which is similar to the problem that this blog entry resolves.
Any guidance or advice would be very very much appreciated,
Thank You
Hi Steve,
Very good examples.
I have a similar problem in my project.
public class Comboboxrenderer extends ComboBox
I have created a custom item renderer combo box and added it to the data grid coulm like below.
var col1:DataGridColumn = new DataGridColumn("@col1");
col1.itemRenderer = new ClassFactory(Comboboxrenderer);
Now I am facing problems in setting the combo box to normal one.
I have written every thing in action script files. Is that creating problems.?
Please help me.
Hi Steve,
Thanks a lot for the tutorial !!
I am stuck with one problem for sometime now, and was hoping if you could help me out.
I am trying to populate a data grid using xml, one of the columns of which is a combobox. However, I also want the combobox to be updated dynamically (depending upon the data from the xml).
I am using flex 3.3 mx.controls.datagrid component.
Here is the code snippet for what I am trying to do:
var propertyColumn:DataGridColumn = new DataGridColumn("Name");
propertyColumn.editable = false;
propertyColumn.width = 60;
var valueColumn:DataGridColumn = new DataGridColumn("Value");
valueColumn.editable = true;
propertyColumn.width = 60;
var unitColumn:DataGridColumn = new DataGridColumn("Unit");
unitColumn.itemRenderer = new ClassFactory(Unit);
Here Unit is an class which implements Combobox
public class Unit extends ComboBox
{
private var _displayList:ArrayCollection;
public function Unit()
{
_displayList = new ArrayCollection();
_displayList.addItem("N/A");
this.selectedIndex = 0;
this.dataProvider = _displayList;
}
public function setDisplayList(list:ArrayCollection):void
{
_displayList = list;
this.dataProvider = _displayList;
this.selectedIndex = 0;
}
}
Then I create an array object which contains a list of values for this, and assign it to the dataprovider of the data grid. All the other values in the grid get displayed properly except for the combobox. The reason I checked was that it creates a new item for the unit when I assign my data to the dataprovider, and does not take my previous data.
I understand that what is happening is correct (because I am doing new ClassFactory(Unit) while creating itemrenderer), but dont know what I need to do to pass the data I provided to the itemrenderer. I tried to add the "override public function set data/dataProvider" functions, but still it did not work.
Any help/feedback that you may give would be really really appreciated !!!
Thanks in advance,
Kapil
Thanks a lot for your tip, it solves my problem [:)]
Thanks mate!!! it solved my problem..
Thanks a lot, Steve !
Your post helped me a lot to keep my mental sanity
Steve, i think you just saved my life. I knew something was being reused, but I couldn’t pinpoint where that was happening. I had a custom cell renderer adding a movie clip to index 0, and thus always putting it behind the last mc. I now clear out previous children before adding a new mc in the renderer. This issue seems to go hand in hand with the crap garbage collection that as3 has.
Anyways, great blog article. it was the lightbulb fix to my problem!
Wow, this helped me out a lot! Thank you so much. I was using this with an image (anytime there was a value was set to 1 it would show an alert icon). However, as soon as I horizontally scrolled the alert icon would jump to random cells. Problem fixed thanks to the super.data = value;