Friday, May 13, 2011

Customizing DefaultGridItemRenderer

This article demonstrates how to customize the way DefaultGridItemRenderer displays its data by overriding the prepare() method.

The most efficient way to render a DataGrid cell is to use DefaultGridItemRenderer (or UITextFieldGridItemRenderer if you're only deploying to Windows) and the most efficient way to configure the displayed elements of any GridItemRenderer  is to override its prepare() method. DataGrid calls the prepare() method each time the renderer is redisplayed and the method is intended to be used to configure the renderer based on its properties, notably the data property.

To configure the text displayed by the DefaultGridItemRenderer we set its label property.  The renderer is-a text field  and it automatically initializes its text property with the value of the label property.  That automatic step happens unconditionally, so setting the DefaultGridItemRenderer text property directly isn't effective.

To override an item renderer's prepare() method, it's necessary to define a subclass.  A complex subclass should be defined in its own file, but simple ones can be defined inline, using the fx:Component tag.  The fx:Component tag essentially creates a new top level scope for an anonymous subclass of its root tag.  In our case the root tag is always DefaultGridItemRenderer, which is to say that we're defining subclasses of DefaultGridItemRenderer.  The  prepare() method override has to be specified with ActionScript code which is defined within the fx:Script element.

Here's an example where each GridColumn's itemRenderer has been defined by a different DefaultGridItemRenderer subclass whose prepare() method initializes the renderer's label and other visual properties.

The DefaultGridItemRenderer subclass definition for the "ID" column is defined as follows.   The renderer's  data property is set to the dataProvider item displayed by all of the renderer's in the same row.  We've specified the GridColumn dataField property, even though it's not used to compute the final value of the renderer's label, so that interactive sorting for this column is based on each item's "key" property.

    <s:GridColumn dataField="key" headerText="ID">
        <s:itemRenderer>
            <fx:Component>
                <s:DefaultGridItemRenderer>
                    <fx:Script>
                        <![CDATA[
                            override public function prepare(hasBeenRecycled:Boolean):void
                            {
                                label = data.name + " (" + data.key + ")";
                            }
                        ]]>
                    </fx:Script>
                </s:DefaultGridItemRenderer>
            </fx:Component>
        </s:itemRenderer>
    </s:GridColumn>


The prepare() method's hasBeenRecycled parameter, which is not used here, is false if the renderer was just created from scratch, and true if the renderer was used before and had been added to the DataGrid's internal free list.

The first column in this example displays the item renderer's rowIndex property, which can be useful when the length of the dataProvider is relatively large. In this case we're not displaying any aspect of the renderer's data, just the index of the row that the renderer appears on.  It's not useful to sort on this column so we've specified sortable="false".

    <s:GridColumn sortable="false" headerText="Row Index">
        <s:itemRenderer>
            <fx:Component>
                <s:DefaultGridItemRenderer>
                    <fx:Script>
                        <![CDATA[
                            override public function prepare(hasBeenRecycled:Boolean):void
                            {
                                label = String(rowIndex);
                            }
                        ]]>
                        </fx:Script>
                </s:DefaultGridItemRenderer>
            </fx:Component>
        </s:itemRenderer>
    </s:GridColumn>

The last column in this example sets some DefaultGridItemRenderer styles.   DefaultGridItemRenderer is-a textfield and supports a more limited set of text styles than most Spark components.  Check the API documentation before assuming that a specific style is supported.

    <s:GridColumn dataField="price" headerText="Price">
        <s:itemRenderer>
            <fx:Component>
                <s:DefaultGridItemRenderer>
                    <fx:Script>
                        <![CDATA[
                            override public function prepare(hasBeenRecycled:Boolean):void
                            {
                                setStyle("color", (data.call) ? 0x000000 : 0xDD0000);
                            }
                        ]]>
                    </fx:Script>
                </s:DefaultGridItemRenderer>
            </fx:Component>
        </s:itemRenderer>
    </s:GridColumn>

This approach, i.e. overriding the prepare() method, to customizing a GridColumn's item renderer is efficient but somewhat verbose.  Defining the item  renderer classes in separate files reduces the GridColumn's MXML markup bulk, but can make the application more cumbersome to read and manage. Item renderers that need to display more than text can be similarly defined with a GridItemRenderer subclass.  In that case using data binding instead of a prepare() method override can make the markup a little easier to read however doing so is less efficient.


4 comments:

  1. Hi Hans,

    Does that mean it's better to override the prepare method with the spark DG instead of overriding the set data method like we used to do with the halo DG?

    ReplyDelete
  2. Yes. The prepare() method is called when the Grid detects that the item renderer needs to be redisplayed. For example it will be called when the renderer's hovered or selected properties change, even if the data property has not.

    ReplyDelete