Customizing List-Based Controls

List-based controls such as lists, data grids, and trees have standard ways in which they display data. For example, a list displays one column of text, data grids display one or more columns of text, and trees display a hierarchical view of data. For many if not most applications, the default ways in which these controls display data are perfectly sufficient. However, there are cases in which you need to alter the displays in one way or another. For example, you may want to display a checkbox in a data grid column rather than standard text.

When you want to customize the way in which a list-based component displays elements, you can use what is called an item renderer. Item renderers allow you to specify what component to use in place of the standard text or text and icon that appear in the component, thus customizing the appearance of the elements in the component. The following components support custom item renderers: List, HorizontalList, DataGrid, Menu, TileList, and Tree.

There are two basic ways in which you can use custom item renderers:

Drop-in item renderers

These are the simplest types of item renderers to implement. With a drop-in item renderer you simply specify a standard UI component to use in a particular column. For example, you can use a checkbox as a drop-in item renderer.

Inline item renderers

These types of item renderers are still rather simple to implement, but they allow you to exert more control over the component. For example, with a drop-in item renderer you cannot specify the property settings for the component, but with an inline item renderer you can.

You can use standard or custom components as item renderers with either of these approaches.

Drop-In Item Renderers

Drop-in item renderers are extremely simple to implement. All you need to do is set the itemRenderer property of the component for which you want to customize the item views. The itemRenderer property should be a reference to a component class you want to use. For example, the following creates a list component that uses a date field component for each item:

<mx:Script>
<![CDATA[
  import mx.collections.ArrayCollection;
  [Bindable]
  private var dataSet:ArrayCollection =
          new ArrayCollection([new Date(2010, 1, 1), new Date(2010, 4, 15)]);
]]>
</mx:Script>

<mx:List itemRenderer="mx.controls.DateField" dataProvider="{dataSet}" 

The results of this are shown in Figure 10-1.

A date field used as an item renderer in a list component

Figure 10-1. A date field used as an item renderer in a list component

All the list-based components that allow you to use item renderers allow you to set the itemRenderer property for the component itself, except in the case of the data grid, which requires that you set the itemRenderer property at the column level. Example 10-11 sets one of the columns of a data grid to display a checkbox.

Example 10-11. Using a drop-in item renderer

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:VBox>
      <mx:DataGrid editable="false">
        <mx:columns>
          <mx:DataGridColumn headerText="Song Title" dataField="title"/>
          <mx:DataGridColumn headerText="Artist" dataField="artist"/>
          <mx:DataGridColumn headerText="In Favorites" dataField="inFavorites"
itemRenderer="mx.controls.CheckBox" />
        </mx:columns>
        <mx:dataProvider>
          <mx:ArrayCollection>
            <mx:Array>
              <mx:Object songId="0" title="Astronaut" artist="David Byrne"
rating="5" inFavorites="true" />
              <mx:Object songId="1" title="Rio" artist="Duran Duran"
rating="3" />
              <mx:Object songId="2" title="Enjoy the Silence"
artist="Depeche Mode" rating="4" />
              <mx:Object songId="3" title="Mesopotamia"
artist="B-52s" rating="5" inFavorites="true" />
            </mx:Array>
          </mx:ArrayCollection>
        </mx:dataProvider>
      </mx:DataGrid>
  </mx:VBox>

</mx:Application>

Drop-in item renderers are ideal when you want to use a simple type of item renderer. However, they have several major limitations.

  • You can use only a handful of standard UI components as drop-in item renderers. Table 10-2 lists those components.

  • The data value for an item always corresponds to one property of the item renderer. In other words, a list-based component is a view for the data model assigned to the dataProvider property. For example, the item value is always assigned to the value of a numeric stepper used as an item renderer. You cannot specify that the item value should be assigned to the maximum property of the numeric stepper.

  • You can't customize the components used as item renderers.

Table 10-2. Drop-in components

Component

Property set by the item value

Button

selected

CheckBox

selected

DateField

selectedDate

Image

source

Label

text

NumericStepper

value

Text

text

TextArea

text

TextInput

text

Inline Item Renderers

Although drop-in item renderers are extremely simple to implement, they are also quite limited in terms of how you can configure them. For instance, in Example 10-11 you can display the checkbox in the data grid columns, but you cannot change any of the properties of the components used as item renderers.

Inline item renderers are a slight step up from drop-in item renderers in that you can configure the settings of the component used as the item renderer. For example, you can use an inline item renderer to set the enabled property of the checkbox to disable it so that the user cannot check or uncheck the box.

Inline item renderers require that you specify the itemRenderer value using nested MXML tags rather than attributes. You must then nest within the itemRenderer tag a Component tag with a nested tag to create the type of component you want to use as an item renderer. Example 10-12 specifies the checkbox item renderer as an inline item renderer. It also applies a label to the checkbox, and it disables the checkbox so that the user cannot select or deselect it.

Example 10-12. Using inline item renderers

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:VBox>
      <mx:DataGrid editable="false">
        <mx:columns>
          <mx:DataGridColumn headerText="Song Title" dataField="title"/>
          <mx:DataGridColumn headerText="Artist" dataField="artist"/>
          <mx:DataGridColumn headerText="In Favorites" dataField="inFavorites">
            <mx:itemRenderer>
              <mx:Component>
                 <mx:CheckBox label="Song in favorites" enabled="false" />
               </mx:Component>
             </mx:itemRenderer>
          </mx:DataGridColumn>
        </mx:columns>
        <mx:dataProvider>
          <mx:ArrayCollection>
            <mx:Array>
              <mx:Object songId="0" title="Astronaut" artist="David Byrne"
              rating="5" inFavorites="true" />
              <mx:Object songId="1" title="Rio" artist="Duran Duran" rating="3" />
              <mx:Object songId="2" title="Enjoy the Silence"
                         artist="Depeche Mode" rating="4" />
              <mx:Object songId="3" title="Mesopotamia"
                         artist="B-52s" rating="5" inFavorites="true" />
            </mx:Array>
          </mx:ArrayCollection>
        </mx:dataProvider>
      </mx:DataGrid>
  </mx:VBox>
</mx:Application>

The Component tag is a powerful MXML tag. It creates an entirely new scope within the MXML document. The code within the Component tag is essentially an MXML component, and the rules that apply to MXML components generally apply to the code within the Component tag. You can have just one root node, and within that root node you can use Style, Script, and all standard MXML tags you could use in an MXML component document. Because the Component tag creates its own scope, you don’t have to worry about conflicts within the Component tag and the document within which the Component tag exists. However, this also means that you cannot reference data from the MXML document within the Component tag. For example, the following will cause a compile error:

<mx:Script>
<![CDATA[
  private var maximumCount:uint = 5;
]]>
</mx:Script>
<mx:List>
  <mx:itemRenderer>
    <mx:Component>
      <mx:NumericStepper maximum="{maximumCount}" />
    </mx:Component>
  </mx:itemRenderer>
</mx:List>

The error occurs because maximumCount is defined in the MXML document, but it is referenced within the Component tag, which has a different scope.

Although the Component tag is powerful, we strongly recommend that you use it only to the extent illustrated by Example 10-12 in which we set the label and enabled properties of the checkbox. If you need to create more sophisticated item renderers, it is far better to define them as MXML or ActionScript components. We’ll look at how to do that next.

Note

You can use a property called outerDocument within a Component tag to reference the MXML document containing the Component tag. You can then reference any public or internal properties, as in the following example:

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

  <mx:Script>
  <![CDATA[
    internal var maximumCount:uint = 5;
  ]]>
  </mx:Script>
  <mx:List>
    <mx:itemRenderer>
      <mx:Component>
        <mx:NumericStepper maximum="{outerDocument.maximumCount}"/>
      </mx:Component>
    </mx:itemRenderer>
  </mx:List>

</mx:Application>

However, although this is possible, it is generally not recommended because it is much clearer to break out sophisticated item renderers into new custom components, as discussed in the next section.

Custom Components As Item Renderers

To exert the most control over item renderers you can use a custom component. Using a custom component (either MXML or ActionScript) you can create extremely sophisticated item renderers. For example, you could create an item renderer that displays a rating using colored shapes, as we’ll do in this section.

A component must implement certain interfaces to work as an item renderer. There are three basic interfaces for item renderers: IListItemRenderer, IDropInListItemRenderer, and IDataRenderer. All item renderers must implement IListItemRenderer and IDataRenderer. Because IListItemRenderer extends IDataRenderer, you simply need to implement IListItemRenderer in most cases. The IListItemRenderer interface requires many getter/setter methods and public methods, and the best way to implement the interface is simply to extend a class that already implements the interface. The following classes already implement the interface: Button, ComboBox, Container, DataGridItemRenderer, DateField, Image, Label, ListBase, ListItemRenderer, MenuBarItem, MenuItemRenderer, NumericStepper, TextArea, TextInput, TileListItemRenderer, and TreeItemRenderer. Because Container implements the interface, you can extend any type of container.

The IDataRenderer interface requires that the implementing class defines a data getter and setter of type Object. The data setter is automatically called every time the data provider is updated and the item renderer needs to update. The data setter is always passed the data provider element corresponding to the item. For example, in a data grid the data is the object representing the row. Even though your custom item renderer component is likely to inherit the data implementation, you’ll generally want to override that implementation.

Example 10-13 is saved in an MXML document called Rating.mxml, and it draws five squares using 10-by-10 canvases. The squares are blue by default, and they are colored red if they are activated by the value of the rating property from the data passed to the component. Notice that this component overrides the data getter and setter. The setter retrieves the rating value, and it draws the canvases with the appropriate colors based on the rating value.

Example 10-13. A custom component for use as an item renderer

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">

  <mx:Script>
    <![CDATA[

      import mx.containers.Canvas;

      private var _data:Object;

      override public function set data(value:Object):void {
        _data = value;
        var rating:uint = uint(value.rating);
        removeAllChildren();
        var canvas:Canvas;
        for(var i:uint = 0; i < 5; i++) {
          canvas = new Canvas();
          canvas.setStyle("backgroundColor", i < rating ? 0xFF0000 : 0x0000FF);
          canvas.width = 10;
          canvas.height = 10;
          addChild(canvas);
        }
      }

      override public function get data():Object {
        return _data;
      }

    ]]>
  </mx:Script>

</mx:HBox>

The MXML application document in Example 10-14 uses the custom component as an item renderer in a data grid using drop-in syntax.

Example 10-14. Using a custom component as an item renderer

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:VBox>
      <mx:DataGrid editable="false">
        <mx:columns>
          <mx:DataGridColumn headerText="Song Title" dataField="title"/>
          <mx:DataGridColumn headerText="Artist" dataField="artist"/>
          <mx:DataGridColumn headerText="Rating" dataField="rating"
itemRenderer="Rating" />
        </mx:columns>
        <mx:dataProvider>
          <mx:ArrayCollection>
            <mx:Array>
              <mx:Object songId="0" title="Astronaut" artist="David Byrne"
rating="5" inFavorites="true" />
              <mx:Object songId="1" title="Rio" artist="Duran Duran" rating="3" />
              <mx:Object songId="2" title="Enjoy the Silence" artist="Depeche
Mode" rating="4" />
              <mx:Object songId="3" title="Mesopotamia" artist="B-52s" rating="5"
inFavorites="true" />
            </mx:Array>
          </mx:ArrayCollection>
        </mx:dataProvider>
      </mx:DataGrid>
  </mx:VBox>
</mx:Application>

Note

If the item renderer MXML or ActionScript class is in a package, you would specify the fully qualified path to the MXML document or class in the itemRenderer property value.

The data grid with the custom item renderer is shown in Figure 10-2 on the following page.

Using a custom item renderer in a data grid

Figure 10-2. Using a custom item renderer in a data grid

Creating Item Editors

When components are editable, they utilize standard text inputs when the user is editing a value. For example, the following code creates a list with editable values simply by setting the editable property to true. But the user can edit the values only by using the standard text input when she clicks on an item.

<mx:List editable="true" width="200" labelField="rating">
  <mx:dataProvider>
    <mx:ArrayCollection>
      <mx:Array>
        <mx:Object songId="0" title="Astronaut" artist="David Byrne"
                   rating="5" inFavorites="true" />
        <mx:Object songId="1" title="Rio" artist="Duran Duran" rating="3" />
        <mx:Object songId="2" title="Enjoy the Silence" artist="Depeche Mode"
                   rating="4" />
        <mx:Object songId="3" title="Mesopotamia" artist="B-52s" rating="5"
                   inFavorites="true" />
      </mx:Array>
    </mx:ArrayCollection>
  </mx:dataProvider>
</mx:List>

You can customize the way in which a user can edit data using an item editor. You assign an item editor using the itemEditor property in exactly the same ways you can set an item renderer using the itemRenderer property. You must also specify a value for the editorDataField property that tells the component which property of the item renderer should be bound to the data provider. The following illustrates how to rewrite the List tag from the preceding example so that it uses a numeric stepper rather than a standard text input to edit the value. Note that it specifies value as the editorDataField because value is the name of the numeric stepper property that should be linked to the data provider.

<mx:List editable="true" width="200" labelField="rating"
         itemEditor="mx.controls.NumericStepper" editorDataField="value">
  <mx:dataProvider>
    <mx:ArrayCollection>
      <mx:Array>
        <mx:Object songId="0" title="Astronaut" artist="David Byrne" rating="5"
                   inFavorites="true" />
        <mx:Object songId="1" title="Rio" artist="Duran Duran" rating="3" />
        <mx:Object songId="2" title="Enjoy the Silence" artist="Depeche Mode"
                   rating="4" />
        <mx:Object songId="3" title="Mesopotamia" artist="B-52s" rating="5"
                   inFavorites="true" />
      </mx:Array>
    </mx:ArrayCollection>
  </mx:dataProvider>
</mx:List>

One question that you might ask when working with the item editor is why you couldn’t simply use an item renderer. For example, it’s possible to list a numeric stepper as the item renderer in the preceding example. The user could update the numeric stepper value used as an item renderer. However, item renderers simply render the data. They do not create data binding with the data provider. Therefore, changing a value in a numeric stepper used as an item renderer will not affect the data provider whereas it will update the data provider when used as an item editor. However, you can tell a component to use the item renderer as the item editor by setting the rendererIsEditor property to true:

<mx:List editable="true" width="200" labelField="rating"
         itemRenderer="mx.controls.NumericStepper"
         rendererIsEditor="true" editorDataField="value">
  <mx:dataProvider>
    <mx:ArrayCollection>
      <mx:Array>
        <mx:Object songId="0" title="Astronaut" artist="David Byrne" rating="5"
                   inFavorites="true" />
        <mx:Object songId="1" title="Rio" artist="Duran Duran" rating="3" />
        <mx:Object songId="2" title="Enjoy the Silence" artist="Depeche Mode"
                   rating="4" />
        <mx:Object songId="3" title="Mesopotamia" artist="B-52s" rating="5"
                   inFavorites="true" />
      </mx:Array>
    </mx:ArrayCollection>
  </mx:dataProvider>
</mx:List>

Any custom component you can use as an item renderer you can also use as an item editor. The only additional rule is that the component must set a public getter/setter method pair as bindable and you must specify that as the editorDataField value for the component using the custom editor. Example 10-15 modifies Rating so that it can be used as an editor. In this example we define Rating.mxml such that it has a bindable data getter and setter, and when the clicks on the canvases, we update the rating property of the data provided correspondingly.

Example 10-15. A custom item editor

<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">

  <mx:Script>
    <![CDATA[

      import mx.containers.Canvas;
      import flash.events.MouseEvent;

      private var _data:Object;

      [Bindable(event="dataChanged")]
      override public function set data(value:Object):void {
        _data = value;
        draw();
        dispatchEvent(new Event("dataChanged"));
      }

      override public function get data():Object {
        return _data;
      }

      public function clickHandler(event:MouseEvent):void {
        _data.rating = uint(event.currentTarget.name);
        draw();
      }

      private function draw():void {
        var rating:uint = uint(_data.rating);
        removeAllChildren();
        var canvas:Canvas;
        for(var i:uint = 0; i < 5; i++) {
          canvas = new Canvas();
          canvas.setStyle("backgroundColor", i < rating ? 0xFF0000 : 0x0000FF);
          canvas.width = 10;
          canvas.height = 10;
          canvas.name = String(i + 1);
          canvas.addEventListener(MouseEvent.CLICK, clickHandler);
          addChild(canvas);
        }
      }

    ]]>
  </mx:Script>

</mx:HBox>

Example 10-16 uses Rating as both the item renderer and the item editor.

Example 10-16. Using a custom item editor

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:VBox>
      <mx:DataGrid editable="false">
        <mx:columns>
          <mx:DataGridColumn headerText="Song Title" dataField="title"/>
          <mx:DataGridColumn headerText="Artist" dataField="artist"/>
          <mx:DataGridColumn headerText="Rating" dataField="rating"
                             itemRenderer="Rating" rendererIsEditor="true"
                              editorDataField="data" />
        </mx:columns>
        <mx:dataProvider>
          <mx:ArrayCollection>
            <mx:Array>
              <mx:Object songId="0" title="Astronaut" artist="David Byrne"
                         rating="5" inFavorites="true" />
              <mx:Object songId="1" title="Rio" artist="Duran Duran" rating="3" />
              <mx:Object songId="2" title="Enjoy the Silence"
artist="Depeche Mode"
                         rating="4" />
              <mx:Object songId="3" title="Mesopotamia" artist="B-52s"
                         rating="5" inFavorites="true" />
            </mx:Array>
          </mx:ArrayCollection>
        </mx:dataProvider>
      </mx:DataGrid>
  </mx:VBox>
</mx:Application>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset