Thinking About Item Renderers

Many Flex apps have DataGrids. Many of those DataGrids want to display something other than plain text which requires the use of custom item renderers. I’ve seen lots of questions and complaining about how hard it is to make simple custom item renderers. I didn’t design it to be that hard, so I decided to take some time and make some examples of how I’d do it to serve as a starting point for some of the things you want to do in DataGrids.
I found the difficulty level to be about what I expected, but along the way I reminded myself of somethings you might find useful when making your own item renderers. What follows are those things you should probably know, and then some examples.
Recycling
Custom Item Renderers does take a mental shift from other kinds of UI development in Flex. This is because item renderers need to be completely data-driven, and because they are not mapped to the dataprovider in a one-to-one fashion. A single instance of an item renderer can end up displaying the data of several different items in your data provider. It might display the first item, then the tenth, then the second as you scroll up and down in the DataGrid. We do this for efficiency reasons: if we made a renderer for every item in the data provider it would consume tons of memory. Therefore we only create item renderers that are visible, plus a few others for measurement and buffering. This has the following implications:
You cannot assume the renderer has been freshly created. When it is time to update the renderer, the components might be holding old values from the item it was rendering before.
You cannot assume that there are only as many renderers created as you see on-screen.
You cannot assume that the first renderer created is for the first visible row.
The most common error I’ve seen is in not resetting values. For example, if you have code that makes the text red if the value is below zero, you might write:

if (data.price < 0)
setStyle("color", 0xFF0000);

This is incorrect. It will work the first time the renderer is created since the default color is black and will work if values are greater than 0, and if the value is less than zero. But once the renderer displays a value less than zero and turns the text red, if it is later asked to renderer a different value that is greater than zero, there is no code to turn the color back to black again. What will happen is that the data will look fine until you start scrolling or receiving updates, then the red will start to appear in funny places. The correct way is to write code like this:

if (data.price < 0)
setStyle("color", 0xFF0000);
else
setStyle("color", 0x000000);

That code will reset the color to black if the price is greater than 0.
Performance
When designing custom item renderers, you should keep performance in mind. While it is simple and fast to take a Canvas, HBox or VBox and put a few components in there and have a renderer, keep in mind that containers are really big and slow and if you are going to have lots of them visible in your DataGrid, you might run into performance problems. We often don’t see them because we develop on really fast machines, but you have to consider what kind of hardware your end-user will have. So, I’d discourage the use of any container from mx.containers in a custom item renderer if there’s going to be more than a half-dozen on screen. Unless you are laying out more than three or four widgets in your renderer, it will be much faster to start with UIComponent and just write some logic to layout the components in their correct positions. Containers from mx.containers also factor in deferred instantiation logic, scrollbars, clipping and a bunch of other things that your probably don’t need to use in your renderer.
That’s why you’ll see that all of the examples use ActionScript instead of MXML. Yes, that means that you can’t use FlexBuilder to code the renderer, and yes, some of these examples could be done in MXML, but it is easy in MXML to do things that generate extra code that you may not need. You’ll notice that I do not use databinding in these examples for the same reason. It is actually a bit wasteful (although very convenient) to use binding on something that isn’t going to change or only change once or twice. Instead, I set the dataprovider on the initialize event (and would do so on a result event if I used HTTPService).
Events
One frequent complaint is that, if your renderer dispatches events, it is hard to find a way to listen to those events. This is true to a degree. We sort of figured that 80% of renderers would be passive. I’m now thinking that number is closer to 50%. However, it isn’t really that hard to get events, or even to be able to hook up to those events in MXML. First, you need Flex 2.0.1 or later. While some have suggested that you use event bubbling to get events out of the DataGrid, I think that’s potentially unscalable. If you don’t guarantee that the event has a unique name, there could be some parent container of DataGrid that uses the same event name and you’ll get runtime errors. Instead, simply dispatch an event from the owner as in:

var event:Event = new ListEvent("myCustomEventName");
event.itemRenderer = this;
owner.dispatchEvent(event);

This event is then dispatched from the DataGrid, and has a reference to the renderer (and therefore the data) that was affected. To write a handler in MXML, you simply have to subclass DataGrid and add event metadata as in:

/**
* Broadcast when the user does something in the item renderer
*/
[Event("myCustomEventName")]
/**
*  Subclass of DataGrid that dispatches a custom event.
*/
public class MyDataGrid extends DataGrid ...
...

DataGridColumns
However, I want to introduce another way of handling events, and that is to use custom DataGridColumns. The DataGridColumn can act as a proxy for item renderers allowing maximum reuse by allowing the specification of parameters on the column that the renderers respond to, and by being a place to listen to for events from the columns. You’ll see many of these examples use subclasses of DataGridColumn that are paired with the custom renderer. One example, the ComboBoxHeader example, uses custom events from the DataGridColumn instead of through the DataGrid.
DataProviders and Collections
It is also possible to write custom DataProviders/Collections. You don’t have to work just with Arrays or XMLLists. The example NameValueTable takes a data object and displays it in a DataGrid by using a custom collection to enumerate the properties based on a ordered list of properties for the data object.
The Examples
These examples come with the usual caveats. They are unsupported, might have bugs, might not work in your app. Also note that future releases of Flex will likely have more powerful DataGrid features built-in. However, if you can’t wait, maybe these examples will help.
BackgroundColor in Item Renderers
Background Color Demo
Background Color Source
While you can use a Canvas and the backgroundColor style to do this, it turns out that the default DataGridItemRenderer is based on TextField and TextField can have solid color backgrounds. This example has a custom DataGridColumn that allows customization of the background color and value as to when display the colored background.
BackgroundColor Changes When Data Changes
Blink When Data Changes Demo
Blink When Data Changes Source
This example is a variation of the BackgroundColor example where the background color is only shown briefly if the data in the renderer changes.
Text Color and Styles in Item Renderers
Text Styles Demo
Text Styles Source
This example changes the color of the text and makes the text bold based on criteria in a styleFunction that works like labelFunction. By specifying different styleFunctions you can get the color and other font styles to be different based on the data.
HTML in an Item Renderer
HTML Renderer Demo
HTML Renderer Source
I’ve seen many people use the Text component as a renderer for rendering HTML (the subset of HTML that Flash supports, that is). It turns out that the default DataGridItemRenderer can handle HTML with a few minor tweaks and is lighterweight than Text.
Sub-Object Item Renderers
Sub-Object Demo
Sub-Object Source
The default DataGridColumn only handles properties on the dataProvider items. It doesn’t know how to look “deeper” for properties in the items’ sub-objects. That’s for performance reasons. Normally, we tell you to use labelFunction, which is a fine solution, but this example shows how a custom DataGridColumn and custom item renderer can also take the extra time to look at sub-objects.
Split Columns/Grouped Columns
Split Column Demo
Split Column Source
Several folks want to see a column split in two with a header over the two columns. Again, this can be done via custom DataGridColumns and custom item renderers. This example allows you to specify two custom sub-DataGridColumns that define the renderers for the sub-columns.
Custom Header Renderers (CheckBox)
CheckBox Header Renderer Demo
CheckBox Header Renderer Source
Making custom header renderers is a bit trickier, especially if they are interactive. You might need to disable certain default behavior in the DataGrid. This example shows how to get a CheckBox as the header for the column.
Custom Header Renderers (ComboBox)
ComboBox Header Demo
ComboBox Header Renderer Source
Another classic custom header is a ComboBox that allows you to pick filters for the DataGrid data. This example uses a custom ComboBox that allows specification of separators in the list of items, to mimic the look some people use in Excel. This example dispatches a custom event via the custom DataGridColumn.
List of CheckBoxes
CheckBox List Demo
CheckBox List Source
This doesn’t use DataGrid, but I put it here to show one way to do this. It uses an inline item renderer to modify the data provider directly. It is tempting to use rendererIsEditor for this functionality, but it feels better if you handle the change event directly instead of relying on the editable renderer workflow which doesn’t update the data provider until after you click out of the renderer.
Name Value Table/Custom Collection
Custom Collection Demo
Custom Collection Source
.
This also doesn’t use DataGrid, but is a good example of how to build a custom collection. In theory, a custom collection could insert groupings into the array of email much like Outlook does. Maybe I’ll prove that in an example some day, but future releases of Flex should have this functionality built-in, and there are some good versions from third-parties already. I put up this example to get you to consider using custom collections to solve some of your problems
Someday there will be other examples available as well (maybe).. Hope these help.
-Alex

29 Responses to Thinking About Item Renderers

  1. Alex,
    Once again I would just like to thank you for all the time your giving the community.
    It’s one thing to know what you are doing and it’s another to get the confidence you know what you are doing by seeing what the engineers are actually doing when ‘they’ extend the classes that they wrote. 🙂
    I don’t think a lot of people on flex coders realize that you are a full time flex framework engineer and you still have time to give all this great advice and help out while still doing a great job programming our futures.
    Once again, thanks.
    Mike
    Mike, we really appreciate all the work you and other community leaders do. Every once in a while my schedule opens up enough for me to keep up with FlexCoders for a bit, but eventually I get buried again have to stop for awhile and I’m glad you guys somehow find the time to consistently be there for others.
    -Alex

  2. Roman Gruhn says:

    Thanks a lot for this great blog. It helps to understand the recycling and usage of item renderer components and also made me think about those column renderers…
    GREAT JOB!

  3. Andrew says:

    It looks like there are threading problems with some of the samples:
    I’m using the Text Styles sample , but with a bigger list that scrolls.
    It works fine, until you start using the scroll wheel, at which point the styles get all mixed up.
    I guess I’m a little surprised all the functions like getTextStyles() aren’t implemented with parameters (like your styleFunction or labelFunction).
    ————
    Ha! Good point! I didn’t follow my own instructions. The stylesFunction doesn’t reset the styles. It should look more like this::
    if (value 45)
    {
    o.color = 0x00FF00;
    o.bold = true;
    o.underline = true;
    }
    else
    {
    o.color = 0;
    o.bold = false;
    o.underline = false;
    }
    I updated the sample code. I also had to add some code to make sure the styles got fetched when changing the renderers data property.
    However, I must note that this isn’t a threading issue, simply one of not handling recycling as I mentioned in the blog post. There is no threading in ActionScript.
    And FWIW, getTextStyles doesn’t take parameters because it is part of the style system, and not a replaceable function like stylesFunction or labelFunction. That’s why we override it to call stylesFunction only when you need it. Otherwise you’d pay a performance cost for generality when you don’t need it.
    -Alex

  4. Ben says:

    Hi Alex,
    Thanks for these examples. It is nice to see the “recommended” way of doing these things, but I can’t help but wonder why information this vital and widely applicable is showing up on an engineer’s blog, 9 months after Flex was released, and only because he was lucky enough to have some free time. To me, this is the type of info that should have been featured in DevNet articles or whatever shortly after the launch.
    I follow numerous blogs and MXNA pretty vigilantly and I’ve never seen these approaches discussed or even mentioned. Anywhere. That seems extremely unfortunate, especially if the approaches people have come up with really do suffer from performance issues as much as you think they might. I think that could potentially reflect very poorly on the platform as a whole, when in fact it is just caused by developers not knowing any better.
    All gripes aside though, I look forward to examining these examples closer in the coming days. One initial question is how do we know when to use validateNow() versus validateProperties(), etc.? I was either somewhat or completely unaware of most of the validation and other methods you used to commit changes.
    Thanks,
    Ben
    —————
    Ben,
    I would have done this sooner if I had time, but we went from 2.0 to 2.0.1 to two separate hotfixes. If it hadn’t been for the fact that my 3.0 feature schedule got pushed off to a different milestone, I wouldn’t have gotten around to this at all.
    I’d been monitoring flexcomponents, but not flexcoders due to volume of emails and it was only because I had enough time to get back on flexcoders that I saw this trend. I had figured folks had figured this stuff out because we shipped the source code, but apparently it was too deeply buried, and I don’t remember these kinds of questions on flexcomponents.
    So I started a blog and blogged. DevNet takes too much time so I think I’ll just keep updating the blog unless you think I should really formalize this stuff.
    Anyway, sorry it was late in coming.
    As for your question on validateNow/validateProperties. The answer is to never call them unless you need to. Because DGItemRenderer is derived from UITextField is has different behavior than UIComponent, so validateProperties is the equivalent of commitProperties. And validateNow() forces validation in the same frame which is expensive unless you are the leaf node of the display tree.
    Thanks for using Flex and helping out on the lists. We really appreciate your time and feedback.
    -Alex

  5. jeff says:

    Hi alex, thanks for these examples. In regards to your checkbox in the datagrid header, I really wish you took this to the next level. A very common use case is to allow a user to select all rows via checkbox (as you’ve shown), and to allow individual selection of rows. Once selected, allow the user to delete the selected rows. I’ve been trying to solve this for too long, and would greatly appreciate a write up, and demo.
    Thanks
    ————
    Jeff, for spam control, your comments aren’t posted until I “approve” them and I was out all day.
    Anyway, your scenario is interesting, not sure how soon i can get to it. You might post it on FlexCoders and see if someone else can figure it out sooner.
    In general, the principle should merge the checkbox list example with the checkbox header example. The checkbox list example shows that you can/should manage the selected state as a field in the dataprovider.
    -Alex

  6. Chris says:

    Alex,

    This a really useful post. The comboBox examples solved a major problem for me. Thanks.

  7. Mike says:

    Hi ,

    I have created a custom Item renderer class that extends the HBox class. This HBox class contains an Image and a Text control. This “HboxRenderer” class is used as an itemrenderer in my Main application class by a List control . Now my question is how do I acesss each HBox instance (in each row of the List control) . In other words I need to modify an Image instance (change the source) on a particualr row of the List control.

    thanks in advance!

    -Mike

    • Alex Harui says:

      Because renderers are recycled, it is generally a bad idea to access the renderers directly. It is better to modify the renderer to know how to modify itself according to information available to it.

  8. Raghuvir Konanki says:

    This is really helpful. Thanks a lot for posting this. I have a small query to make. I’ve used the split columns for my datagrid. I want make the left and right columns editable. The problem I’m facing here is on single click of any grid…
    a. Its not able to pick up the value thanks to the split up of columns i.e. left and right
    b. From my observation its making the editable and not and editable seperately.

    Any thoughts ?

    Thanks,
    Raghu

  9. Raghuvir Konanki says:

    b. From my observation its making the whole column editable on click of either left or right and hence neither is it able to make each column editable separately nor is it picking up the value.

    Thanks,
    Raghu

  10. Usha says:

    Alex,

    Thanks for a very useful tutorial, I learned how to dispatch a event from a renderer, but I am wondering if I can dispatch an event from one headerrenderer instance to all the other headerrenderer instances? I have a scenario where clicking on one datagridcolumn (headerrenderer) should dispatch an event to all other headerrenderer instances. I been looking all the forums for a solution to this and couldn’t find it. Is it even possible to dispatch an event from one headerrenderer to all other headerrenderer instances?If so,could you kindly suggest a way to do it.

    Thanks,
    Usha

    • Alex Harui says:

      I would probably get all headerrenderers to listen to the DataGrid and have the one headerRenderer dispatch an event from the DataGrid

  11. Kitty says:

    Why is it so darn difficult to do something that is so simple in any other language! All I want is to list a chart of accounts with group headings, just like the old fashioned “control break processing”. Yet Flex developers are telling me this IDE is the best thing in the whole wide world yet they get frustrated and narky when I say I just want to make a nice looking chart of accounts with summary lines throughout, not a tree view, not extra columns, just a simple plain old group header line (row) within my rows and columns, I don’t want it to expand up or down, I don’t want to play a movie either. Like a colspan tag half way through a html tables rows. I keep seeing posts crying out for help to do this, yet no answers. So far, Flex is nothing more than a glorified platform for playing mc’s, it is not even close to an enterprise platform in terms of functionality, ease of use etc etc. No wonder my flex “guru’s” get scared and go quiet when I show them financial report layouts!

    • Alex Harui says:

      It sounds like you want AdvancedDataGrid and GroupingCollection? Also, I think some third parties have financial report libraries.

      • Kitty says:

        Hi Alex, I’ve tried an advanced DataGrid and have explored all possible base options without going down the path of making a custom version. I can see endless examples showing how to have nested column headers, how to add in/define groups, how to make summary rows in a tree view style, either by adding extra columns or by displaying on a separate line within the column being summed. But this is not even close to what I am trying to do. An Access Report, InfoMaker report, a PB report and so forth allow for group heading lines/rows to be defined easily and simply, you can even have a tree view data window where sub groups have different levels of nesting/children. Trying this in Flex caused an error. And the Flex Guru’s tell me that all child groups must be the same, ie: have same number of nesting levels and cannot have nulls/varying data within sub groups. This all seems a little strange or maybe I’m just pushing the boundaries of what a data grid is capable of?
        To do this sort of thing in other web based languages is quite simple,get your data from the db, loop through the data, when your control fields change print out the group or sub group summary row, print out the header row for the next (new) group and keep processing through your results. I am a beginner (if that) when it comes to Flex/Flash/Cold Fusion and are finding it very difficult to do something, that to me, is a basic programming fundamental, regardless of language, anything programmed for the corporate world demands groups and group headings/summaries to be displayed as thus mentioned.
        Are you able to point me in the right direction for some help, some examples or where to find the third parties financial reports libraries? Even if you know of a good book that explains the basis for enterprise level applications developed in FlashBuilder4 with Cold Fusion as the application server.
        To me the online help is of a very very very basic nature and focused on everything “mickey mouse” or groovy movie clip related, not much that is remotely real world corporate application relevant, you know plain boring trade reporting, reconciliations etc!.
        Thank-you for replying to my post.

  12. Bo says:

    I am a java developer that recently migrated over from gwt to flex for our frontend. Although I find many of the flex features and ease of backend integration (blazeds, gui design, datagrid performance) order of magnitude better, there are some big issues with flex that’s making me reconsider my decision, and the lack of documents/support only adds to the frustration. Not all of us do this just for fun factors, when there are deliverable deadlines to meet, googling hours to find solutions to some obscure issue that worked by default on all other gui tools except flex, is a luxury.

    1) What is the logic behind completely revamp the syntax mx: to s: + fx: in FB4. This makes fb4 not compatible with ANY existing codebase – all the examples and codes on the net accumulated over the years that worked perfectly no longer compiles in fb4.

    Then when you convert the sample code to the new syntax and get stumped over some obscure problem, you are left wondering if it’s because of the mx to s conversion or something else, wasting more time.

    2) I read this article and understand the performance reasoning behind itemrenderer, however this implementation defiles human logic. GUI widgets/design need to follow common sense user flow. When you drag/drop rows over or scroll in a datagrid and it automatically resets all your existing combobox/textfield losing the user inputs, that makes no sense. We are 4 years into flex, and yet this is still the default behavior.

    Even worse, there is no clear definition and workaround in the official help. I debugged my flex code and googled for almost 2 hours trying to figure out what is going on, until finally stumbled upon the right keyword “itemrenderer + recycling” and found this blog that explained the behavior. Yet there is still no elegant solution.

    Above is brilliant and simple, except it doesnt work due to the default behavior of the itemrenderer recycling, and we are left to our own devices to fix what is suppos to be a common sense default behavior for any datagrid widget.

    Mind…boggles.

    • Alex Harui says:

      1) Our backward compatibility story is not as good as Java’s. We can break stuff between releases if we think it is for the greater good. We want that flexibility because we need to innovate faster with our set of resources than we would if we had to be as strict as Java. That said, all the mx stuff currently still runs so I don’t fully understand your issue. If you move to spark then you are using a next-generation set of components, just like using a different UI toolkit in Java and then yes, things that break might be because you are using the new library.

      2) Flash is designed primarily to support animation in a browser as a plug-in. Back when Flex first got started, performance overhead from our runtime environment was significant enough that the only way to deal with 100 rows of data was to virtualize and recycle. Now with Spark, you have the option of not virtualizing the renderers. It remains the default behavior because we get lots more complaints when folks suck in 10000 rows off the server and things die that way. But now, 4 years later, you have the option of creating a renderer per row if you think you have the memory.

      • Bo says:

        1) The issue is for new flex developers, most cases we learn by looking and running the examples. So a simple helloworld datagrid itemrenderer code nugget posted all over the web has for example , if the code is pasted to fb4 it will not run as it’s now with the new namespace. A parallel would be jdk 1.7 changing the package System, so System.out.println is now NewSystem.out.println. Imagine what that will do..

        2) Can you please add the comment about default behavior/virtualizing option to the itemrenderer’s official help, so when you tab on the component to get the description, it’s right there. Would save many hours of frustration for a lot of people.

        i didnt want to override the widget with ununcessary code just to fix this, so got rid of the custom styles and used mx:itemEditor instead. For anyone having the same problem if you just want standard widgets to appear in datagrid, you can use mx:itemEditor instead of mx:itemRenderer, which doesnt have those problems. Google itemeditor vs itemrenderer will give you plenty explanation.

        • Alex Harui says:

          1) Yup, we realize the impact, but have decided to make the trade-off.

          2) Please file a documentation bug.

  13. prithish says:

    Hi Alex,

    My requirement is , in the datagrid one of the columns comes as ppre sorted how do i add a gradient color to that column only ..i have used
    grid.setStyle(“headerColors”, [“#F1F6F9”, 0xFFFFFF]); but this seems to work with the all the columns of the grid ,:(

  14. billp says:

    I have used some custom filter headers in an application that uses pods similar to the old flex pod example…
    Having some memory issues and wondering how to throw away the headerrenderers or at least clear out the detail data so the memory impact is minimized….

    The issue occurs when switching pods the number of instances of header renederers grows to the number of columns in dynamic datagrids. I am able to clear out the dataprovider for the datagrid and the columns array but the header renderers remain in memory
    Thoughts?

  15. Denis says:

    Alex, thanks for all your online contributions for Flex devs. I am struggling to find out the cause of a memory leak where instances of a custom itemRenderer in a mx:dataGrid are not being garbage collected fully. I have gone through all your post related to GC and made sure that there are no eventlisteners lingering in the renderers. The renderer is essentially a combination of two labels and one UIComponent which I use to draw on using graphics. Can you suggest what may be the culprit?

  16. Denis says:

    Alex, to add to the previous comment, the leak happens when I assign a fresh set of columns to the datagrid that already has a columnset. The new columns use the same type of renderer but the renderers are not fully reused and many are lingering in the memory – the count of instances increases continously as I repeat assigning sets of columns to the datagrid.
    Thanks in advance for your help.

  17. Nitin says:

    I render graphs (line graph and bar graph (stacked) to be precised) in AdvandedDataGrid. The data for these graph comes as initial snapshot and there after gets updated at some intervals. The Line graph gets new additional data for the plot with last value and max till now highlighted. The bar graphs shown in columns have their scale displayed in header of those columns. To acheive the same, I have created 5 renderers, 1 for line graph and 2 for bar graph column values and 2 for Header that displays the scale.
    I am experiencing performance degradation and when I scroll, i see linegraph in cell values overwritten by other row’s data. Same does not happen if I scroll using scroll bar down/up arrow slowly. Each row have different data and keeps on updating. How can I avoid this problem?

    Following code to specify the item renderers.