Thursday, June 23, 2011

Creating a Custom DataGrid GridItemRenderer with Data Binding


This article describes how to create a custom DataGrid item renderer with GridItemRenderer and data binding.

The GridItemRenderer class is a Group that implements IGridItemRenderer and can be used as the top level "container" for a custom item renderer.  Its data property is the dataProvider item for the row the item renderer appears on and its components can configure themselves by binding to the data property, or by overriding the renderer's prepare() method and configuring themselves imperatively with ActionScript code.  All of the item renderers in this example use binding, which makes the code a little easier to read and modify. It's also less efficient than using a prepare() method override.   The performance difference is most noticeable when the DataGrid is configured so that a large number of item renderers are visible.

DataGrid item renderers are always sized to exactly fit the cell that they're rendering: their size matches the width of their column and the height of their row. The DataGrid enables interactive column resizing by default, so all of the item renderers need to defend against the possibility that their width will become arbitrarily small.  The first column's item renderer just contains a Label.  By specifying maxDisplayedLines="1" we ensure that the Label will truncate its text to fit the width it's given. The other columns' item renderers specify clipAndEnableScrolling="true" which just means that their fixed size contents will always be clipped to the bounds of the GridItemRenderer. This property -enables- scrolling, which means that one could actually scroll the contents of the item renderers when their width gets small, by setting the renderer's horizontalScrollPosition property.  We do not do as much here.


The application's control bar "shake" slider can be used to change the the dataProvider items.
The "Name" column's item renderer demonstrates a special case: if the renderer contains a text component with id="labelDisplay", GridItemRenderer automatically sets the labelDisplay's text property to the value of data[column.dataField] per the GridItemRenderer label property.  The  first column also demonstrates using the styleName property to configure the Label's left, right, top, and fontSize styles.

<s:GridColumn dataField="name" headerText="Name">
    <s:itemRenderer>
        <fx:Component>
            <s:GridItemRenderer>
                <s:Label id="labelDisplay" maxDisplayedLines="1" styleName="nameColumn"/>
            </s:GridItemRenderer>
        </fx:Component>
    </s:itemRenderer>
</s:GridColumn>

We've used styleName here to refer to a set of styles that could have also have been set inline.   The style is defined like this:

<fx:Style>
    .nameColumn {
        fontSize: 18;
        left: 5;
        right: 5;
        top: 9;
    }      
</fx:Style>

The "Statistics" column's item renderer demonstrates how binding can be used to  configure the geometry of graphic elememnts.   The data items' min, max, and value properties have been normalized to the range [0 100] to keep the bindings simple.  The bar chart s:Rect elements specify scaleY="-1" so that the rectangles grow from the bottom of the renderer upwards.

<s:GridColumn dataField="value" headerText="Statistics">
    <s:itemRenderer>
        <fx:Component>
            <s:GridItemRenderer clipAndEnableScrolling="true">
                <s:Group left="5" top="5" bottom="5">
                    <s:Ellipse x="0" y="0" width="30" height="30">
                        <s:stroke>
                            <s:SolidColorStroke color="0x272F32" weight="2"/>
                        </s:stroke>
                        <s:fill>
                           <s:SolidColor color="0x9DBDC6"/>
                        </s:fill>
                    </s:Ellipse>
                    <s:Line rotation="{(data.value / 100) * 360}" transformX="15" transformY="15"
                            xFrom="15" yFrom="15" xTo="27" yTo="15">
                        <s:stroke>
                            <s:SolidColorStroke color="0xFF3D2E" weight="3"/>
                        </s:stroke>
                    </s:Line>
                    <s:Rect x="40" y="30" scaleY="-1" width="15" height="{(data.min / 100) * 30}">
                        <s:fill>
                             <s:SolidColor color="0xFF3D2E"/>
                        </s:fill>
                    </s:Rect>
                    <s:Rect x="60" y="30" scaleY="-1" width="15" height="{(data.max / 100) * 30}">
                        <s:fill>
                            <s:SolidColor color="0xFF3D2E"/>
                        </s:fill>
                    </s:Rect>
                </s:Group>
            </s:GridItemRenderer>
        </fx:Component>
    </s:itemRenderer>
</s:GridColumn>

The "Value" column's item renderer allows one to change data item's value property with a Slider.  The binding is specified with "@{data.value}", which indicates that it's bidirectional, so changing the slider also change the dataProvider item.

<s:GridColumn dataField="value" headerText="Value">
    <s:itemRenderer>
        <fx:Component>
            <s:GridItemRenderer clipAndEnableScrolling="true">
                <s:HSlider left="5" right="5" verticalCenter="0"
                        minimum="{data.min}" maximum="{data.max}" value="@{data.value}"/>
            </s:GridItemRenderer>
        </fx:Component>
    </s:itemRenderer>
</s:GridColumn>

The "Call" column's item renderer demonstrates a slightly more complicated component layout and it also demonstates how the data item's value can be both displayed and edited.

<s:GridColumn dataField="call" headerText="Call">
    <s:itemRenderer>
        <fx:Component>
            <s:GridItemRenderer clipAndEnableScrolling="true">
                <s:HGroup left="5" top="9" right="5" verticalAlign="baseline">
                    <s:CheckBox selected="@{data.call}"/>
                    <s:RadioButton selected="{data.call}" enabled="false"/>
                    <s:TextInput text="{data.call}" enabled="false"/>
                </s:HGroup>
            </s:GridItemRenderer>
        </fx:Component>
    </s:itemRenderer>
</s:GridColumn>

3 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Dear Hans,

    I am using flex 4.6, can we read/write particular cell value in spark data grid. If it is possible then may i know how we can achieve that.

    ReplyDelete
  3. Thanks for sharing this useful info. Keep updating same way.
    Regards,Siddu online Training

    ReplyDelete