The Spark DataGroup and Spark SkinnableDataContainer containers

The Spark DataGroup and Spark SkinnableDataContainer containers take as children any components that implement the IVisualElement interface and are subclasses of DisplayObject. However, these containers are primarily used to take data items as children. Data items can be simple data items such String and Number objects, and more complicated data items such as Object and XMLNode objects.

An item renderer defines the visual representation of the data item in the container. The item renderer converts the data item into a format that can be displayed by the container. You must pass an item renderer to a DataGroup or SkinnableDataContainer container.

The main differences between the DataGroup and SkinnableDataContainer containers are:
  • SkinnableDataContainer can be skinned. The DataGroup container is designed for simplicity and minimal overhead, and cannot be skinned.

  • DataGroup can be a child of the Scroller control to support scroll bars. Create a skin for the SkinnableDataContainer to add scroll bars.

The default layout class of the DataGroup container is BasicLayout. The default layout class of the SkinnableDataContainer class is VerticalLayout. For complete reference information, see the ActionScript 3.0 Reference for the Adobe Flash Platform.

Creating a Spark DataGroup and Spark SkinnableDataContainer container

You use the <s:DataGroup> and <s:SkinnableDataContainer> tags to define a DataGroup and SkinnableDataContainer container. Specify an id value if you intend to refer to a component elsewhere in your MXML, either in another tag or in an ActionScript block.

The DataGroup and SkinnableDataContainer container are examples of data provider components. Data provider components require data for display or user interaction. To provide data, assign a collection which implements the IList interface, such as an ArrayList, ArrayCollection, or XMLListCollection object, to the container’s dataProvider property. For more information on using data providers, see Data providers and collections.

The dataProvider property takes an array of children, as the following example shows:

<s:DataGroup itemRenderer=...>  
    <s:dataProvider>  
        <mx:ArrayList> 
            <fx:String>Dave Jones</fx:String> 
            <fx:String>Mary Davis</fx:String> 
            <fx:String>Debbie Cooper</fx:String> 
        </mx:ArrayList>  
    </s:dataProvider>  
</s:DataGroup>

If you are using Flex Components as children of the container, you can specify them as the following example shows:

<s:DataGroup itemRenderer=...>  
    <s:dataProvider>  
        <mx:ArrayList> 
            <s:Button/> 
            <s:Button/> 
            <s:Button/> 
        </mx:ArrayList>  
    </s:dataProvider>  
</s:DataGroup>
Because dataProvider is the default property of the DataGroup and SkinnableDataContainer container, you do not have to specify a <s:dataProvider> child tag. Therefore, you can write the example as shown below:
<s:DataGroup itemRenderer=...> 
    <mx:ArrayList> 
        <fx:String>Dave Jones</fx:String> 
        <fx:String>Mary Davis</fx:String> 
        <fx:String>Debbie Cooper</fx:String> 
    </mx:ArrayList> 
</s:DataGroup>

You can mix different types of data items in a container, or mix data items and Flex components. For example, you might mix String, Object, and XML data in the same container. However, you must define an item renderer function to apply the correct item renderer to the child. For more information, see Using an item renderer function with a Spark container.

You can skin the SkinnableDataContainer in the same way that you skin the container. For an example of a skin, see Creating a Spark SkinnableContainer container.

Using a default item renderer with a Spark container

The DataGroup and SkinnableDataContainer containers require an item renderer to draw each container child on the screen. By default, the DataGroup and SkinnableDataContainer containers do not define an item renderer. You can configure the containers to use the item renderers provided by Flex, or define your own custom item renderer.

Flex ships with two item renderers: spark.skins.spark.
  • spark.skins.spark.DefaultItemRenderer Converts its data item to a single String for display in a Spark Label control. It is useful when displaying a scalar data item, such as a String or a Number, that can be easily converted to a String.

  • spark.skins.spark.DefaultComplexItemRenderer Displays a Flex component in a Group container. Each component is wrapped in its own Group container. Therefore, it is useful when the children of the container are visual elements, such as Flex components.

The following example uses the DefaultItemRenderer with a DataGroup container:

<?xml version="1.0" encoding="utf-8"?>
<!-- containers\spark\SparkDataGroupContainerDefaultRenderer.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:DataGroup itemRenderer="spark.skins.spark.DefaultItemRenderer"> 
        <s:layout>
            <s:VerticalLayout/>
        </s:layout> 
        <mx:ArrayList>
            <fx:String>Dave Jones</fx:String>
            <fx:String>Mary Davis</fx:String>
            <fx:String>Debbie Cooper</fx:String>
        </mx:ArrayList>
    </s:DataGroup> 
</s:Application>

The executing SWF file for the previous example is shown below:

Each data item of the container is a String. Because you use the DefaultItemRenderer with the container, each String appears in the container in a Label control.

If the data item is of type Object or is a data type that is not easily converted to a String, then you either have to convert it to a String, or define a custom item renderer to display it. For more information, see Passing data to a Spark item renderer.

The following example shows a DataGroup container where all its children are Flex components. The DataGroup class uses the DefaultComplexItemRenderer to display each child:

<?xml version="1.0" encoding="utf-8"?>
<!-- containers\spark\SparkDataGroupContainerSimpleVisual.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:DataGroup itemRenderer="spark.skins.spark.DefaultComplexItemRenderer"> 
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
        <mx:ArrayList>
            <s:Button/>
            <s:Button/>
            <s:Button/>
            <s:Button/>
        </mx:ArrayList>           
    </s:DataGroup> 
</s:Application>

The executing SWF file for the previous example is shown below:

Because you use the DefaultComplexItemRenderer with the container, each Button control appears in the container nested in its own Group container. By wrapping each control in a Group container, the item renderer can support selection highlighting for the individual children. However, if you do not want each control to appear in its own Group container, set the item renderer to null, as shown below:

<s:DataGroup itemRenderer="{null}"> 
Note: If you are only displaying visual elements in a DataGroup or SkinnableDataContainer container, you should instead use the Group or SkinnableContainer containers.

You might be able to create your application by using just the DefaultItemRenderer and DefaultComplexItemRenderer classes. However, you typically define a custom item renderer if your data items are not simple values, or if you want more control over the appearance of your container children. For more information on creating a custom item renderer, see Define a custom Spark item renderer.

Adding and removing children at runtime

To modify the children of the DataGroup and SkinnableDataContainer containers at runtime, modify the dataProvider property. The following example uses event handlers to add and remove container children by calling the addItem() and removeItemAt() methods on the dataProvider property:
<?xml version="1.0" encoding="utf-8"?>
<!-- containers\spark\SparkDataGroupContainerAddRemoveChild.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
    
    <fx:Script>
        <![CDATA[
        
            private function addDGChild():void {
                var newChild:String = "new child";
                myDG.dataProvider.addItem(newChild);
                
                addDG.enabled = false;
                removeDG.enabled = true;
            }
            
            private function removeDGChild():void {
                myDG.dataProvider.removeItemAt(3);
                
                addDG.enabled = true;
                removeDG.enabled = false;
            }
        ]]>
    </fx:Script>

    <s:DataGroup id="myDG" 
        itemRenderer="spark.skins.spark.DefaultItemRenderer"> 
        <s:layout>
            <s:VerticalLayout/>
        </s:layout> 
        <mx:ArrayList>
            <fx:String>Dave Jones</fx:String>
            <fx:String>Mary Davis</fx:String>
            <fx:String>Debbie Cooper</fx:String>
        </mx:ArrayList>
    </s:DataGroup>
    
    <s:Button id="addDG" label="Add Child" 
        click="addDGChild();"/> 
    <s:Button id="removeDG" label="Remove Child"
        enabled="false" 
        click="removeDGChild();"/> 
</s:Application>

The executing SWF file for the previous example is shown below:

For more information on using data providers, see Data providers and collections.

Using virtualization with Spark DataGroup and SkinnableDataContainer

A DataGroup or SkinnableDataContainer container can represent any number of children. However, each child requires an instance of an item renderer. If the container has many children, you might notice performance degradation as you add more children to the container.

Instead of creating an item renderer for each child, you can configure the container to use a virtual layout. With virtual layout, the container reuses item renderers so that it only creates item renderers for the currently visible children of the container. As a child is moved off the screen, possible by scrolling the container, a new child being scrolled onto the screen can reuse its item renderer.

To configure a container to use virtual layout, set the useVirtualLayout property to true for the layout associated with the container. Only the DataGroup or SkinnableDataContainer with the VerticalLayout, HorizontalLayout, and TileLayout supports virtual layout.

Note: If you define an itemRendererFunction to determine the item renderer for each data item, Flex will not reuse item renderers. The itemRendererFunction must examine each data item and create the item renderers as necessary for the specific data item type. While Flex does not reuse item renderers, it only creates enough item renderers for the currently visible data items. For more information, see Creating a recyclable item renderer for virtual layout.

There are a few differences between the way a layout class works when virtual layout is enabled and when it is disabled:

  • A layout with virtual layout enabled does not support the layout’s major axis percent size property. This axis corresponds to the percentHeight property for the VerticalLayout class, and the percentWidth property for the HorizontalLayout class.

  • A container using a virtual layout that contains few children whose sizes vary widely can respond poorly to interactive scrolling using the scroll thumb. No performance degradation occurs when scrolling using the scroll arrows or by clicking in the scroll track. Responsiveness improves as the variation in size decreases or the number of children increases.

Use virtual layout when the cost of creating or measuring a DataGroup is prohibitive because of the number of data elements or the complexity of the item renderers.

For more information on creating item renderers, see Define a custom Spark item renderer.