20
Aug
07

Formatting a Flex DataGrid control using a custom item renderer

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.

View MXML

<?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 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>

PriceLabel.as

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", (data.@price <= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
        }
    }
}

View source is enabled in the following example.


32 Responses to “Formatting a Flex DataGrid control using a custom item renderer”


  1. 1 stef Sep 14th, 2007 at 7:04 am

    Hi,

    Thanks a lot man !

  2. 2 judah Oct 8th, 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.

  3. 3 Richie Nov 19th, 2007 at 10:33 am

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

  4. 4 peterd Nov 19th, 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

  5. 5 sinnus Nov 21st, 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.

  6. 6 peterd Nov 21st, 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

  7. 7 Gareth Dec 12th, 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”

  8. 8 Catto Dec 18th, 2007 at 11:54 am

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

  9. 9 Randy Mar 23rd, 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.

  10. 10 Paulo Apr 1st, 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.

  11. 11 Bill Shirley Apr 22nd, 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,

  12. 12 Greg C May 6th, 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!!

  13. 13 peterd May 6th, 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] <= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
            }
        }
    }
    

    Peter

  14. 14 Greg C May 8th, 2008 at 12:48 pm

    Thanks Peter. Worked great!

  15. 15 Greg C May 9th, 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!

  16. 16 Greg C Jun 12th, 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

  17. 17 peterd Jun 12th, 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

  18. 18 marktplaats Jul 3rd, 2008 at 8:36 pm

    Some more great info, thanks for the tips.

  19. 19 Joshua Jul 23rd, 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/

  20. 20 janet Aug 20th, 2008 at 1:32 pm

    Hi guys,

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

    Any ideas?

    Janet

  21. 21 peterd Aug 20th, 2008 at 2:30 pm

    janet,

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

    Peter

  22. 22 janet Aug 21st, 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)…

  23. 23 peterd Aug 22nd, 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

  24. 24 donkelito Sep 4th, 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

  25. 25 Pierre Sep 27th, 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

  26. 26 peterd Sep 27th, 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

  27. 27 Dan Nov 7th, 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?

  28. 28 Chris Jan 5th, 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!

  29. 29 Chris Jan 5th, 2009 at 12:49 pm

    Sussed it!

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

  30. 30 Mandy Jan 21st, 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

  31. 31 Shreyas Mar 23rd, 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

  32. 32 Phillip Molaro Apr 6th, 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.

Leave a Reply

This blog is terrible at eating HTML tags. If you plan on posting code/XML, please escape your "<" characters as "&lt;" and your ">" characters as "&gt;".




Badge Farm

  • Powered by Redoable 1.2
  • Cornify
  • Feeds burnt by Feedburner
  • Feed