Formatting a Flex DataGrid control using a custom item renderer

by Peter deHaan on August 20, 2007

in CurrencyFormatter, DataGrid

The following example formats a column in a Flex DataGrid and uses a custom item renderer to color the text red in a cell if a price is below $0. If the item is greater than $0, the test is displayed in black. The price column is also formatted using a custom label function, which uses a CurrencyFormatter, and finally, the data grid column uses a custom sort function to properly sort numeric columns.

Full code after the jump.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2007/08/20/formatting-a-flex-datagrid-control-using-a-custom-item-renderer/ -->
<mx:Application name="DataGridColumn_itemRenderer_test "
        xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">
 
    <mx:Script>
        <![CDATA[
            import mx.controls.dataGridClasses.DataGridColumn;
            import mx.utils.ObjectUtil;
 
            private function price_labelFunc(item:Object, column:DataGridColumn):String {
                return currencyFormatter.format(item.@price);
            }
 
            private function price_sortCompareFunc(itemA:Object, itemB:Object):int {
                return ObjectUtil.numericCompare(itemA.@price, itemB.@price);
            }
        ]]>
    </mx:Script>
 
    <mx:XML id="itemsXML">
        <items>
            <item name="Item 1" price="1.32" />
            <item name="Item 2" price="-12.23" />
            <item name="Item 3" price="4.96" />
            <item name="Item 4" price="-0.94" />
        </items>
    </mx:XML>
 
    <mx:Style>
        .centered {
            text-align: center;
        }
    </mx:Style>
 
    <mx:CurrencyFormatter id="currencyFormatter"
            precision="2"
            useNegativeSign="false" />
 
    <mx:DataGrid id="dataGrid" dataProvider="{itemsXML.item}">
        <mx:columns>
            <mx:DataGridColumn dataField="@name"
                    headerText="Name:"
                    headerStyleName="centered" />
 
            <mx:DataGridColumn dataField="@price"
                    headerText="Price:"
                    textAlign="right"
                    headerStyleName="centered"
                    labelFunction="price_labelFunc"
                    sortCompareFunction="price_sortCompareFunc"
                    itemRenderer="PriceLabel" />
        </mx:columns>
    </mx:DataGrid>
 
</mx:Application>

And the custom item renderer, PriceLabel.as, is as follows:

/** http://blog.flexexamples.com/2007/08/20/formatting-a-flex-datagrid-control-using-a-custom-item-renderer/ */
package {
    import mx.controls.Label;
    import mx.controls.listClasses.*;
 
    public class PriceLabel extends Label {
 
        private const POSITIVE_COLOR:uint = 0x000000; // Black
        private const NEGATIVE_COLOR:uint = 0xFF0000; // Red
 
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
 
            /* Set the font color based on the item price. */
            setStyle("color", (parseFloat(data.@price) <= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
        }
    }
}

View source is enabled in the following example.

{ 41 comments… read them below or add one }

1 stef September 14, 2007 at 7:04 am

Hi,

Thanks a lot man !

Reply

2 judah October 8, 2007 at 12:20 pm

Hi Peter,

Thanks for the example! :)

Do you know how to create an item renderer in a datagrid column that creates different components based on the data?

For example,

column 1…….column 2
————————-
item 1………[checkbox]
item 2………[checkbox]
item 3………[combobox]
item 4………[textinput]
item 5………[textinput]

data:
array = [{label:'item 1',type:'checkbox'},etc]

When I scroll or sort the headers everything screws up.

Reply

3 Richie November 19, 2007 at 10:33 am

How do I center align a checkbox inside a datagrid? It always aligns left…Please help.

Reply

4 peterd November 19, 2007 at 10:50 am

Richie,

I’m not sure, but check out Alex Harui’s excellent blog entry, “More Thinking About Item Renderers”, which has demo and full source code.

Peter

Reply

5 sinnus November 21, 2007 at 1:22 am

Could you say me build number of Flex complier. I got another behavior of your example. Price column and cell have the same align – “right”.
My build number: Version 2.0.1 build 155542.
Thanx.

Reply

6 peterd November 21, 2007 at 8:21 am

sinnus,

All of the examples in this blog were built with various beta/nightly builds of the Flex 3 SDK.

Peter

Reply

7 Gareth December 12, 2007 at 7:57 am

@Ritchie,
I’ve centered a checkbox in a datagrid column before by adding an HBox around it and using horizontalAlign=”center”

Reply

8 Catto December 18, 2007 at 11:54 am

Hey Now,
Nice post it was helpful to me.
Thx 4 the info,
Catto

Reply

9 Randy March 23, 2008 at 10:49 am

An improvement to the label function might be to change the return currencyFormatter.format(item.@price); t0 return currencyFormatter.format(item[column.dataField]);

This should make the label function usable on any column that can be formatted as currency rather than hardcoding in the column field.

Reply

10 Paulo April 1, 2008 at 4:45 am

Hi Peter, how can I do to change the background of entire line, based in some information in the fields? Thanks.

Reply

11 Bill Shirley April 22, 2008 at 1:43 pm

@judah (6 months later),

i think you need to override the set data accessor – the same instances of the renderer get reused, you’ll need to change the contents of the view hierarchy when the data changes,

Reply

12 Greg C May 6, 2008 at 1:52 pm

Flex newbie… Is there a way to make this package more generic so the colomn nmae doesn’t have to be embedded in the package? This way any column that needed this formating “itemRenderer” could use this package.

Thanks for all of your examples!!

Reply

13 peterd May 6, 2008 at 2:27 pm

Greg C,

Try something like this:

package {
    import mx.controls.Label;
    import mx.controls.dataGridClasses.DataGridListData;
    import mx.controls.listClasses.*;
 
    public class PriceLabel extends Label {
 
        private const POSITIVE_COLOR:uint = 0x000000; // Black
        private const NEGATIVE_COLOR:uint = 0xFF0000; // Red
 
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
 
            var dField:String = DataGridListData(listData).dataField;
            /* Set the font color based on the item price. */
            setStyle("color", (data[dField] &lt;= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
        }
    }
}

Peter

Reply

14 Javier Rodriguez September 4, 2009 at 4:02 am

Hi , i was wondering where the listData object comes from?

Thanks in advance for your help

Reply

15 Greg C May 8, 2008 at 12:48 pm

Thanks Peter. Worked great!

Reply

16 Greg C May 9, 2008 at 8:56 am

Peter,
I am trying to apply your above example to similar code for another use of a datagrid itemRenderer. So I was wondering if you could point me in the right direction if I wanted the value of an external Control to affect the threshold point of the values that are colored.

So if I had an hslider it could be used to dynamically change the value of the cut-off point of the colored items in that column of the datagrid.

So basically instead of:

setStyle("color", (data[dField] <= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);

it would be:

setStyle("color", (data[dField] <= X) ? NEGATIVE _COLOR : POSITIVE _COLOR);

X being the value that would be represented by the position of the hslider.

Does this make sense?

Since I am new to this whole package/class thing I am not sure how to access that external value inside of the package that is being called by the itemRenderer of a grid column.

Thanks again!

Reply

17 Greg C June 12, 2008 at 6:07 am

Peter, Once again sorry for the double post… Any thoughts regarding how I can go about passing a dynamic value from a control into this package? I have tried a couple things and none of which seem to work.

Thanks,
Greg C

Reply

18 peterd June 12, 2008 at 7:23 am

Greg C,

Probably not the best solution, but you could try using Application.application.slider.value. I’m not sure how well it would work since the itemRenderers wouldn’t necessarily be updated when the slider changes.

Peter

Reply

19 marktplaats July 3, 2008 at 8:36 pm

Some more great info, thanks for the tips.

Reply

20 Joshua July 23, 2008 at 12:41 pm

Note that a similar solution (for background-color) is posted by jlafferty at:
http://butterfliesandbugs.wordpress.com/2007/07/11/using-an-itemrenderer-to-change-the-background-of-a-datagrid-cell/

Reply

21 janet August 20, 2008 at 1:32 pm

Hi guys,

In this example, how can be sorted the price column as a number?

Any ideas?

Janet

Reply

22 peterd August 20, 2008 at 2:30 pm

janet,

The price column is sorting as numbers, isn’t it?

Peter

Reply

23 janet August 21, 2008 at 12:46 pm

Nope, when the prices are loaded you get ($1.32, $12.23, $4.96, $0.94), if you sort the price column you get ($12.23, $0.94, $1.32, $4.96) instead of ($12.23, $4.96, $1.32, $0.94), and if you sort again to get the numbers from descendant to ascendant you have($4.96, $1.32, $0.95, $12.23) instead of ($0.95, $1.32, $4.96, $12.23)…

Reply

24 peterd August 22, 2008 at 7:20 am

janet,

Interesting. I see the same default sort as you (1.32, -12.23, 4.96, -0.94) — the items are unsorted and appear in the same order they were specified in the data provider.
If I sort by price (descending), I get the following: -12.23, -0.94, 1.32, 4.96 (or Item 2, Item 4, Item 1, Item 3). This sort is correct since the biggest negative values are first and the biggest positive values are last.
If I sort by price (ascending), I get the following: 4.96, 1.32, -0.94, -12.23 (or Item 3, Item 1, Item 4, Item 2). This sort is correct since the biggest positive values are first and the biggest negative values are last.

Actually, are you sure your numbers are right? 0.94 and 12.23 are both negative numbers. So I wouldn’t expect the price column to sort as 12.23, 4.96, 1.32, 0.94 since both 0.94 and 12.23 are negative numbers in the data provider. I’d expect 4.96, 1.32, -0.94, -12.23.

Peter

Reply

25 donkelito September 4, 2008 at 4:37 am

hi,

i have developed a similar example. the difference is that my item renderer is a linkbutton extend class. The override function is the next:

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
    super.updateDisplayList(unscaledWidth, unscaledHeight);
 
    var spattern:String = listData.label;
 
    var tic:TextImageClass = new TextImageClass(spattern);
    if (tic.tranform()) {
        this.setStyle("paddingRight",Number(35));
        if (tic.displayObject != null) {
            this.setStyle("icon",tic.imageClass);
            this.labelPlacement = ButtonLabelPlacement.LEFT;
        }
        if (tic.uiTextField != null) {
            this.label = tic.uiTextField.text;
        }
    }
}

my problem is when i sort a column. When i click the column header, the data rows are changed but not sorted… but i move the mouse cursor over the rows… magically… the data in the columns are going to appearing sorted.

any idea?
thanks, congratulations, it´s a great website

Reply

26 Pierre September 27, 2008 at 7:02 am

Hello,

Thank for this very interesting example.

If a change the source with an arraycollection, what kind of modifications i must do in the PriceLabel.as field ?

Thanks

Best Regards

Reply

27 peterd September 27, 2008 at 8:33 am

Pierre,

In PriceLabel.as, it looks like I hard-coded the column data field (data.@price) into the item renderer. Of course, you could create a new subclass for each different data field (CostLabel.as, etc.), but that would probably not be the best approach.

I think the better approach would be to look at my coworker Alex Harui’s solution to this, as he is ridiculously smarter than I am: “Thinking About Item Renderers”. Specifically, I was reading the Text Color and Styles in Item Renderers section. He shows how you can extend both the DataGridColumn and DataGridItemRenderer classes, and create a custom stylesFunction() method which evaluates the data in a column without hard-coding specific column names.

Peter

Reply

28 Dan November 7, 2008 at 10:01 am

Can an ItemEditor dispatch their own events? If so, how do you set an event listener to capture the event?

Reply

29 Chris January 5, 2009 at 9:32 am

I’m trying to use similar code but have the logic based on the value of an attribute “D” in my xml data but I cannot figure out for the life of me how to access the attribute in the .as file for the current column (I’m using the renderer for multiple columns). My XML looks something like this:

<cols>
    <colA D="1">123</colA>
    <colB D="2">123</colB>
</cols>

using data..@D gives all D values for the current cols node (i.e. “12″). Can anyone advise?

Thanks!

Reply

30 Chris January 5, 2009 at 12:49 pm

Sussed it!

data[DataGridListData(listData).dataField].@D);

Reply

31 Mandy January 21, 2009 at 7:49 pm

I have try your example.Very useful.Thanks lots. I still have one question and hope you can help. Now I have 2 string, lets say AAA and BBB which I want to change their color out of many data. I try to use :

 setStyle("color", (data.id == "AAA") ? NEGATIVE_COLOR : POSITIVE_COLOR);
 setStyle("color", (data.id == "BBB") ? NEGATIVE_COLOR : POSITIVE_COLOR);

but it only change the color of BBB.
So what should I do to change both field?
thanks

Reply

32 Shreyas March 23, 2009 at 11:54 pm

Hi,

Very nice blog … Is there any way to change the text color of the entire row of a datagrid based on data with out creating a itemrenderer for each column. I have around 10 columns, and want to change the color of text to red or green based on some data.

Thanks,
Shreyas

Reply

33 Phillip Molaro April 6, 2009 at 1:52 pm

Is there any concern of this method being a performance hit? I put a trace statement in my updateDisplayList() in my custom component while I was getting things going. I noticed that if I had 10 items, on load i got 20-30 traces. If I move my mouse over I get more traces. Same with a select. I understand why this happens, but is there not a way to set it and forget it? Does the DG loose the settings each time it redraws?? I am still sort of amazed that there isn’t just a property to set for a row color.

Reply

34 Niladri August 21, 2009 at 2:59 am

Hi peterd
Instead of comparing the data value with 0, If i want to do comparison of two column value and set the row value red if column1 value is less than column2 value then how can i do?

Reply

35 Peter deHaan August 21, 2009 at 7:15 am

@Niladri,

Try something like this:

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2007/08/20/formatting-a-flex-datagrid-control-using-a-custom-item-renderer/ -->
<mx:Application name="DataGridColumn_itemRenderer_test"
        xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">
 
    <mx:Script>
        <![CDATA[
            import mx.utils.ObjectUtil;
 
            private function col1_sortCompareFunc(itemA:Object, itemB:Object):int {
                return ObjectUtil.numericCompare(itemA.@col1, itemB.@col1);
            }
 
            private function col2_sortCompareFunc(itemA:Object, itemB:Object):int {
                return ObjectUtil.numericCompare(itemA.@col2, itemB.@col2);
            }
        ]]>
    </mx:Script>
 
    <mx:XML id="itemsXML">
        <items>
            <item name="Item 1" col1="1" col2="0" />
            <item name="Item 2" col1="0" col2="1" />
            <item name="Item 3" col1="0" col2="0" />
            <item name="Item 4" col1="-4" col2="44" />
            <item name="Item 4" col1="44" col2="-4" />
            <item name="Item 4" col1="2" col2="111" />
            <item name="Item 4" col1="2" col2="1" />
            <item name="Item 4" col1="1" col2="222" />
        </items>
    </mx:XML>
 
    <mx:DataGrid id="dataGrid"
            dataProvider="{itemsXML.item}"
            rowCount="{dataGrid.dataProvider.length}">
        <mx:columns>
            <mx:DataGridColumn dataField="@name"
                    headerText="Name:" />
 
            <mx:DataGridColumn dataField="@col1"
                    headerText="Col1:"
                    sortCompareFunction="col1_sortCompareFunc"
                    itemRenderer="PriceLabel" />
 
            <mx:DataGridColumn dataField="@col2"
                    headerText="Col2:"
                    sortCompareFunction="col2_sortCompareFunc"
                    itemRenderer="PriceLabel" />
        </mx:columns>
    </mx:DataGrid>
 
</mx:Application>

And the custom item renderer, PriceLabel.as, is as follows:

/** http://blog.flexexamples.com/2007/08/20/formatting-a-flex-datagrid-control-using-a-custom-item-renderer/ */
package {
    import mx.controls.Label;
    import mx.controls.listClasses.*;
 
    public class PriceLabel extends Label {
 
        private const POSITIVE_COLOR:uint = 0x000000; // Black
        private const NEGATIVE_COLOR:uint = 0xFF0000; // Red
 
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
 
            /* Set the font color based on the item price. */
            setStyle("color", (parseFloat(data.@col1) < parseFloat(data.@col2)) ? NEGATIVE_COLOR : POSITIVE_COLOR);
        }
    }
}

Peter

Reply

36 Niladri August 21, 2009 at 12:37 pm

Thanks a lot Peter.

37 Niladri August 24, 2009 at 6:20 am

Peter, I have one doubt.I’m allocating data field for the data grid column dynamically. So some time it will not have data field as col1,may be some time it will have column field as col3 or col4.So my question is instead of referring data field in PriceLabel.as can I use the column id of those two column in PriceLabel.as

Reply

38 Javier Rodriguez September 4, 2009 at 3:24 am

I would like to know if I could pass some objects to the constructor of the customItemRender class because I wasnt able to do it, the compiler throws an error. I attach my code below:
customItemRender class:

package core {
import mx.containers.VBox;
import mx.controls.TextInput;

public class customItemRender extends VBox {
public function customItemRender(_TextInput:TextInput, _TextInput2:TextInput){
//TODO: implement function
super.addChild(_TextInput);
super.addChild(_TextInput2);
}
}
}

Actionscript code for creating an AdvancedDataGrid itemRender:

AdvancedDataGridColumn.itemRenderer = new ClassFactory(customItemRender(_TextInput1,_TextInput2));

Thanks in advance for your help,

Regards Javier

Reply

39 sss4 September 16, 2009 at 9:29 pm

Thanks for the awesome example, I think the fact that you’re still getting comments on a two year old post speaks to the usefulness of it.

I have multiple columns that I need to run through a label function, can you think of a quick way to make the label function re-usable?

TIA,

~S

Reply

40 sss4 September 16, 2009 at 10:42 pm

Never mind, I got it:

private function cur_labelFunc(item:Object, column:DataGridColumn):String {
                return cF.format(item[column.dataField]);
            }

See any problems with that?

Thanks again,

~S

Reply

41 Michael December 30, 2009 at 10:56 am

Great example, yet again you saved the day for me.

Reply

Leave a Comment

Sorry, this blog is terrible at eating HTML comments.
If you're pasting any HTML/XML/MXML code, you need to convert your < characters to &lt; and your > characters to &gt; .

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Anti-Spam Protection by WP-SpamFree

Previous post:

Next post: