Chapter 17. Using list controls

Most Flex applications are designed for the purpose of presenting and managing data in some form. As a result, one of the most popular families of visual controls in the Flex framework includes those known as list controls.

A list control is defined as a component that has a dataProvider property that allows you to populate the control with dynamic data. The data provided to a list control can be in the form of either hierarchical or relational data, and the type of data you want to present frequently determines which control you use. In addition to being able to display relational or hierarchical data, list controls have a common set of properties, methods, and events that allow the user to select one or more items with mouse and keyboard gestures.

The mx.controls. List component is the most fundamental of these controls. This component behaves like an HTML >select< control and displays a list of data items to the user as a list box. After you learn how to use the List control, you have most of the information you need to use other such controls. You can populate controls with data, listen for events indicating that the user has selected or started to drag data, set common styles, and so on.

Note

This chapter describes use of the single column list controls: List and ComboBox. It includes information on how to populate these controls with data, how to control data presentation with custom generation of item labels and renderers, and how to handle events indicating that the user wants to select and manipulate data. The unique capabilities of other list controls, including the DataGrid, TileList, and HorizontalList, are described in Chapter 18.

Note

To use the sample code for this chapter, import the chapter17.zip project from the Web site files into any folder on your disk.

Table 17.1 shows a list of the components that have the ability to display dynamic data and support user interaction using the list control model.

Table 17.1. The List Controls

Control

Description

AdvancedDataGrid

A new feature of the Flex 3 class library, this component implements all the features of the List and DataGrid components, but adds the ability to group and aggregate data and can sort on multiple columns. This component is part of the Flex Data Visualization components and is available only with a Flex Builder Professional license.

ComboBox

This component presents a drop-down list of data items. The presentation of this component is similar to an HTML <select> control that has its size property set to 1. This component's editable property, when set to true, allows the user to enter an arbitrary string instead of selecting an item from the list.

DataGrid

This component presents a grid with multiple rows and columns. It is used to present data received from a server-side database or other data source that uses the spreadsheet-like rows-and-columns structure of relational database tables.

HorizontalList

This component presents a horizontal list of data items, typically rendered with a custom item renderer.

List

This component presents a list box of data items. The presentation of this component is similar to an HTML <select> control that has its size property set to a value greater than 1.

OlapDataGrid

Another new feature of the Flex 3 class library, this component expands on the AdvancedDataGrid and supports presentation of results from an OLAP query.

TileList

This component presents a grid of data items, typically rendered with a custom item renderer.

Tree

This component presents hierarchical data, commonly supplied by the contents of an XML file.

This chapter describes the details of working with all list controls, focusing on the simpler components: List, ComboBox, TileList, and HorizontalList. The following chapter describes the details of working with the DataGrid and related components.

Tip

In addition to the components listed in Table 17.1, the Flex 3 class library adds a set of list controls designed for use in AIR applications. These controls provide the user with the ability to inspect and manipulate files and directories in the local file system and cannot be used in Flex applications that are deployed over the Web. They include the FileSystemList, FileSystemComboBox, FileSystemDataGrid, and FileSystemTree components.

Most of the information in this chapter and in Chapter 18 about list and DataGrid controls applies equally to these AIR-based controls, but these controls add functionality that allows them to populate their data from the directory and file contents of the local file system. They also implement additional properties and methods that are designed to support their unique purpose.

Tip

There are two unique components that extend a class named ComboBase and therefore must be considered members of the family of list controls as well. The ColorPicker control is designed to allow selection of a color value from a grid of "Web-safe" colors, and the DateField control presents a pop-up calendar control. The components aren't often thought of as list controls, but they support the same set of properties, methods, and events as their cousins.

Each of the list controls has its own unique visual presentation and behavior. As the developer, you select the control most suited to your application's requirements.

Figure 17.1 shows examples of the List, ComboBox, DataGrid, and Tree controls, each using the same set of data as its data provider.

Commonly used list controls

Figure 17.1. Commonly used list controls

Note

The application displayed in Figure 17.1 is available in the Web site files as ListControls.mxml in the chapter17 project.

Using Data Providers

The data you provide to a list control must be in the form of an ActionScript object, but for most purposes you typically provide data that's been wrapped in one of the data collection classes: either the ArrayCollection class for data that's in rows and columns or the XMLListCollection class for hierarchical data.

The List and ComboBox controls are distinguished from the DataGrid and its related controls in that they present only a single column of data. They can present data from a collection of complex objects, but by default they present only one value in each list item. In contrast, the DataGrid control is designed to present data in multiple columns.

Using hard-coded data providers

You can embed data in a Flex application for use by either a specific instance of a list control or as a separate data object that's then linked to a control through a binding expression. Hard-coding means that you declare actual data in the code, rather than retrieving it from an external data source at runtime.

Caution

As described in Chapter 16, when you embed data in a Flex application, the compiled application file expands accordingly. You should embed data only when it's a small amount of content and won't change during the lifetime of the application.

Nesting hard-coded data in a data provider

You can nest hard-coded data in the declaration of a list control's dataProvider by declaring the property with child-element syntax rather than attribute syntax. The following code presents a List control populated with a hard-coded data provider containing simple String values:

<mx:List id="sizeList">
   <mx:dataProvider>
     <mx:String>Small</mx:String>
     <mx:String>Medium</mx:String>
     <mx:String>Large</mx:String>
     </mx:dataProvider>
</mx:List>

You also can declare the dataProvider with hard-coded collections of complex objects by nesting multiple <mx:Object> declarations within the <mx:dataProvider> tag set:

<mx:List id="stateList">
   <mx:dataProvider>
       <mx:Object>
        <label>California</stateName>
        <capitol>Sacramento</capitol>
      </mx:Object>
      <mx:Object>
         <label>Oregon</stateName>
<capitol>Salem</capitol>
         </mx:Object>
         <mx:Object>
           <label>Washington</stateName>
           <capitol>Olympia</capitol>
         </mx:Object>
      </mx:dataProvider>
    </mx:List>

Modifying data with the ArrayCollection API

At runtime, data that's hard-coded within a dataProvider is automatically wrapped inside an ArrayCollection object, so the ArrayCollection API can be used to access and manipulate the data. Even though the original data is hard-coded, this ActionScript statement code would add a new item to the List object's dataProvider when it contains simple String values:

sizeList.dataProvider.addItem('Extra Large'),

And this code would add a new item when it contains complex objects:

stateList.dataProvider.addItem({state:'New York','Albany'});

The application in Listing 17.1 uses a List object with a hard-coded data provider and then allows the user to add data to the object with the ArrayCollection.addItem() method.

Example 17.1. A List control with hard-coded data

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   layout="vertical">
  <mx:List id="sizeList" width="300">
     <mx:dataProvider>
         <mx:String>Small</mx:String>
         <mx:String>Medium</mx:String>
         <mx:String>Large</mx:String>
      </mx:dataProvider>
   </mx:List>

<mx:HBox>
 <mx:Label text="New Item:"/>
 <mx:TextInput id="itemInput"/>
<mx:Button label="Add Item"
  click="sizeList.dataProvider.addItem(itemInput.text)"/>
</mx:HBox>

</mx:Application>

Note

The code in Listing 17.1 is available in the Web site files as ListWithHardCoded Data.mxml in the chapter17 project.

Declaring separate data objects with MXML tags

You also can provide hard-coded data to a list control using the <mx:Model>, <mx:Array>, and <mx:ArrayCollection> tags. Regardless of which of these tags you select, the dataProvider of the List object is transformed to an ArrayCollection at runtime, so you should select whichever tag makes most sense to you, keeping in mind that only an ArrayCollection will broadcast updates to properties of complex objects at runtime.

The application in Listing 17.2 declares an Array with the <mx:Array> tag and then provides the data to the List object through a binding expression. Notice that even though the data is declared as an Array, you can still manipulate it at runtime by referencing the List object's dataProvider as an ArrayCollection.

Example 17.2. A List control with data provided through a binding expression

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
      layout="vertical">
  <mx:Array id="myData">
     <mx:String>Small</mx:String>
     <mx:String>Medium</mx:String>
     <mx:String>Large</mx:String>
</mx:Array>
<mx:List id="sizeList" width="300" dataProvider="{myData}"/>
<mx:HBox>
     <mx:Label text="New Item:"/>
     <mx:TextInput id="itemInput"/>
     <mx:Button label="Add Item"
       click="sizeList.dataProvider.addItem(itemInput.text)"/>
</mx:HBox>
</mx:Application>

Note

The code in Listing 17.2 is available in the Web site files as ListWithHardCoded Array.mxml in the chapter17 project.

Using dynamic data providers

Data retrieved from an external source, such as the results of a remote server call through the Remote Procedure Call (RPC) components, or data retrieved from a local database (for an AIR desktop application) is typically stored in an ArrayCollection. As described in Chapter 16, the ArrayCollection object is typically declared in ActionScript code with the [Bindable] metadata tag or in MXML code.

In ActionScript code, the declaration looks like this:

[Bindable]
private var myData:ArrayCollection = new ArrayCollection();

And in MXML, it looks like this:

<mx:ArrayCollection id="myData"/>

Tip

Data objects that are declared in MXML are immediately instantiated and always bindable.

Regardless of how the ArrayCollection object is declared, by making it bindable, you make it possible to pass the data to a List control with a simple binding expression:

<mx:List id="sizeList" dataProvider="{myData}"/>

Using RPC components

You can choose to retrieve data dynamically from many sources, including the Flex framework components that are grouped together as the RPC classes. These classes are distinguished from each other by the data format they use to communicate with a remote server:

  • HTTPService:This class sends simple HTTP requests to URLs that return data formatted as simple text or XML. For example, a call to an RSS feed from a blog or content-based Web site would be executed using the HTTPService class.

  • WebService:This class retrieves data from a server with calls formatted in the industry standard SOAP format.

  • RemoteObject:This class sends and receives messages formatted in Action Message Format (AMF). This binary format is defined by Adobe and implemented in many of its server products, including LiveCycle Data Services, BlazeDS, and ColdFusion.

These components and their methodologies are described starting in Chapter 24. All, however, are capable of returning data sets in the form of ArrayCollection objects that are suitable for use as List control data providers.

Tip

The AMF data format was published by Adobe Systems in 2007 to support development of independent application server products that are compatible with Flex-based and Flash-based applications.

Retrieving local data in AIR applications

If you're building an AIR-based desktop application, you can retrieve data from local XML files using the File and FileStream classes or from the local SQLite embedded database with classes such as SQLConnection and SQLStatement. These classes aren't designed to return data in the ArrayCollection format directly; you typically need to manually move data into your data objects with explicit ActionScript code.

Controlling List Item Labels

If a List control's data provider contains simple String values, these values are displayed on each item by default. If the data provider contains complex objects (either instances of the ActionScript Object class or of your own custom value object classes), you can determine the text labels that are displayed in a List control's items using one of these strategies:

  • The labelField property lets you point to a specific named property of each object whose values should be displayed.

  • The labelFunction property lets you customize each item's label with your own ActionScript code.

Using the labelField property

Most List controls support the labelField property. This property allows you to indicate which of the named properties of data items in the control's data provider is displayed at runtime.

The default value of labelField is label. As a result, if the data provider's objects have a property named label, that property's value is displayed. In the following code, the stateData Array contains data objects with a label property. The List control displays the label property's value on each of its items:

<mx:Array id="stateData">
    <mx:Object>
      <mx:label>CA</mx:label>
      <mx:capitol>Sacramento</mx:capitol>
   </mx:Object>
  <mx:Object>
    <mx:label>OR</mx:label>
    <mx:capitol>Salem</mx:capitol>
</mx:Object>
</mx:Array>
  <mx:List id="stateList" dataProvider="{stateData}"/>

More commonly, the complex objects in the ArrayCollection have names that are determined by the structure of a database table, XML file, value object, or other existing data source. If you forget to set the labelField property on a List control that displays complex data objects, the control displays labels consisting of a set of [] characters wrapped around the word object and the object's data type. If the data item is cast as an ActionScript Object, the result looks like this:

[object Object]

As shown in Figure 17.2, the results aren't particularly useful, even when working with a value object class.

A List control displaying a complex data object with no labelField setting

Figure 17.2. A List control displaying a complex data object with no labelField setting

To fix this behavior, you explicitly set the List control's labelField to the property you want to display:

<mx:Array id="stateData">
     <vo:StateVO>
      <vo:state>CA</vo:state>
      <vo:capitol>Sacramento</vo:capitol>
  </vo:StateVO>
  <vo:StateVO>
      <vo:state>OR</vo:state>
      <vo:capitol>Salem</vo:capitol>
    </vo:StateVO>
  </mx:Array>
   <mx:List id="stateList" dataProvider="{stateData}"
        labelField="state"/>

Figure 17.3 shows the same List control, this time displaying the value of the property named in the control's labelField property.

A List control displaying a complex data object with the labelField set to one of the properties of the data provider's complex data objects

Figure 17.3. A List control displaying a complex data object with the labelField set to one of the properties of the data provider's complex data objects

The application in Listing 17.3 uses the List control's labelField property to determine which property value of each data object is displayed at runtime.

Example 17.3. Using the labelField property

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
      xmlns:vo="vo.*">
    <mx:Array id="stateData">
         <vo:StateVO>
            <vo:state>CA</vo:state>
            <vo:capitol>Sacramento</vo:capitol>
        </vo:StateVO>
        <vo:StateVO>
              <vo:state>OR</vo:state>
              <vo:capitol>Salem</vo:capitol>

</vo:StateVO>
<vo:StateVO>
  <vo:state>WA</vo:state>
   <vo:capitol>Washington</vo:capitol>
 </vo:StateVO>
</mx:Array>
<mx:List id="stateList" width="200"
     dataProvider="{stateData}" labelField="capitol"/>
</mx:Application>

Note

The code in Listing 17.3 is available in the Web site files as UsingLabelField.mxml in the chapter17 project.

Using the labelFunction property

Most List controls implement the labelFunction property to allow you to customize the label that appears on each of the control's items at runtime. The labelFunction property points to the name of a function that follows a specific signature:

[access modifier] function [functionName](item:Object):String

The access modifier for a custom label function can be anything you like, although when you're calling the function from within the same application or component in which it's defined, the access modifier is typically set to private because it's most often used only from within. The name of the function's only argument (item in the example syntax) can be anything you like, but it should be typed as either an Object or a custom class implementing the Value Object design pattern, depending on what type of data is stored in your List's dataProvider collection. And the function always returns a String, because its purpose is to generate a label for the List control's visual items.

At runtime, the List control calls the named function each time it needs to render an item visually. It passes the current data object to the custom function as its item argument and then displays the returned String value. This is an example of a function that's compatible with the labelFunction architecture:

private function getStateLabel(item:StateVO):String
{
  return item.capitol + ", " + item.state;
}

The application in Listing 17.4 displays a List control where each visual item's label is generated by the custom getStateLabel() function.

Example 17.4. Using the labelFunction property

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
      xmlns:vo="vo.*">
<mx:Script>
    <![CDATA[
         import vo.StateVO;
         private function getStateLabel(item:StateVO):String
        {
          return item.capitol + ", " + item.state;
        }
]]>
</mx:Script>
<mx:Array id="stateData">
   <vo:StateVO>
   <vo:state>CA</vo:state>
   <vo:capitol>Sacramento</vo:capitol>
</vo:StateVO>
<vo:StateVO>
     <vo:state>OR</vo:state>
     <vo:capitol>Salem</vo:capitol>
</vo:StateVO>
<vo:StateVO>
     <vo:state>WA</vo:state>
     <vo:capitol>Washington</vo:capitol>
   </vo:StateVO>
</mx:Array>
<mx:List id="stateList" width="200"
     dataProvider="{stateData}" labelFunction="getStateLabel"/>
</mx:Application>

Note

The code in Listing 17.4 is available in the Web site files as UsingLabelField.mxml in the chapter17 project.

The resulting application is shown in Figure 17.4. Notice that each of the List control's labels is generated using both of the data object's named properties, concatenated with literal strings to separate the values.

A List control displaying String values calculated in a labelFunction

Figure 17.4. A List control displaying String values calculated in a labelFunction

Tip

The DataGrid component doesn't implement the labelField or labelFunction properties directly. Instead, these properties are implemented in the DataGridColumn component so you can easily customize the presentation of individual columns.

List Control Events and Properties

All List controls support these events to notify you of user actions and other important updates to a control:

  • change:Notifies you that the user has selected an item using either a mouse or keyboard gesture

  • dataChange:Notifies you that the content of the control's data property has changes, which typically is relevant when the control is used in a custom item renderer or item editor

  • itemClick:Notifies you that an item in the list control has been clicked

  • itemDoubleClick:Notifies you that the user double-clicked on an item in the List control when the doubleClickEnabledproperty is set to true

  • itemRollOut:Notifies you that the mouse pointer moved out of an item in the List control

  • itemRollOver:Notifies you that the mouse pointer moved over an item in the List control

List controls also support these properties that can be used to detect which data the user currently has selected:

  • allowMultipleSelections:Boolean: When set to true, this allows the user to select more than one item at a time by holding down Ctrl while clicking items.

  • selectedIndex:int:This is the numeric index of the currently selected item.

  • selectedIndices:Array:This is an array of indices of the currently selected items, when the List control's allowMultipleSelectionproperty is set to true.

  • selectedItem:Object:This is the data object underlying the List control's currently selected row or cell.

  • selectedItems:Array:This is an array of currently selected objects, when the List control's allowMultipleSelection property is set to true.

  • doubleClickEnabled:Boolean: When this property is set to true, the List control detects double clicks on its items and dispatches a doubleClick event.

In addition, each List control supports unique events and properties designed for that control's specific purpose and capabilities.

Tip

The ComboBox control does not support the allowMultipleSelection, selectedIndices, or selectedItems properties. Because it uses a drop-down interface to present its list, the user can select only one data item at a time.

Handling User Data Selections

When a user selects items in a List control, he's indicating that he wants to use the selected item's underlying data. When this occurs, the List control dispatches a change event. After this event occurs, you can use the control's selectedItem and selectedIndex properties to detect which item has been selected.

Using the change event

The change event is implemented in all List controls. It dispatches an event object typed as mx.events.ListEvent, which has a rowIndex property that indicates by index which data item was selected by the user.

You can detect which data item was clicked by the user by using the event object's rowIndex property and passing it to the getItemAt() method of the ArrayCollection data provider:

changeMessage = "You clicked on " +
  event.target.dataProvider.getItemAt(event.rowIndex);

This technique notifies you that the user clicked an item, but it doesn't always indicate that the item returned in the expression event.target.dataProvider.getItemAt(event.rowIndex) is currently selected. In most List controls, the user can hold down Ctrl and click to deselect an item, in which case you get a change event that can't be distinguished from the event that occurs when selecting an item.

Using the selectedItem property

A better approach is to use the List control's selectedItem property, which always returns a reference to the currently selected item. In the event the user has deselected all items in a List control, the selectedItem property returns null:

if (event.target.selectedItem == null)
{
   changeMessage = "None selected";
}
else
{
  changeMessage = "You selected " + event.target.selectedItem;
}

The application in Listing 17.5 uses a List control and a change event listener. Each time the change event is dispatched by the List control, an event handler function inspects the control's selectedItem and displays a message indicating which item (if any) is currently selected.

Example 17.5. Using the change event and selectedItem property

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml>
  <mx:Script>
    <![CDATA[
      [Bindable]
      private var changeMessage:String="None selected";
      private function changeHandler(event:Event):void
       {
        if (event.target.selectedItem == null)
        {
          changeMessage = "None selected";
        }
        else
        {
           changeMessage = "You selected " +  event.target.selectedItem;
       }
   }
]]>
</mx:Script>
<mx:Array id="myData">
     <mx:String>Small</mx:String>
     <mx:String>Medium</mx:String>
     <mx:String>Large</mx:String>
 </mx:Array>
<mx:List id="sizeList" width="200" dataProvider="{myData}"
     change="changeHandler(event)"/>
<mx:Label text="{changeMessage}" fontSize="12"/>
</mx:Application>

Note

The code in Listing 17.5 is available in the Web site files as ChangeEventDemo.mxml in the chapter17 project.

When testing this application, try holding down Ctrl and clicking an item that's already selected. You should see the message "None selected" displayed, because the control's selectedItem property now returns null.

Using the selectedIndex property

All List controls implement the selectedIndex property, which returns the index position of the control's currently selected item. Because all indexing in ActionScript starts at 0, if the first item is selected the selectedIndex property returns 1, the second returns 2, and so on. When you use a List or ComboBox control in a data entry form, you can place a data item as the first item in a list that indicates that the user is selecting all options:

<mx:ComboBoxid="categoryList"change="changeHandler(event)">
   <mx:dataProvider>
        <mx:String>All Categories</mx:String>
        <mx:String>Comedy</mx:String>
        <mx:String>Drama</mx:String>
        <mx:String>Action</mx:String>
        <mx:String>Horror</mx:String>
  </mx:dataProvider>
</mx:ComboBox>

The following code would detect whether the user has selected the first item, indicating she wants all categories or a specific category:

private function changeHandler(event:Event):void
  {
    if (categoryList.selectedIndex == 0)
    {
       Alert.show("You selected all categories", "Everything!");
    }
    else
    {
      Alert.show("You selected " + categoryList.selectedItem,
         "One Thing!");
    }
  }

If no items are currently selected in a List control, the selectedIndex property returns a value of −1. This is particularly useful when you want to detect a state where the user hasn't yet selected a value from a List or DataGrid control:

private function changeHandler(event:Event):void
{
  if (categoryList.selectedIndex == −1)
  {
     Alert.show("You haven't selected anything!", "Nothin!");
  }
  else
  {
   Alert.show("You selected " + categoryList.selectedItem,
     "One Thing!");
  }
}

Tip

When using a ComboBox with its editable property set to the default value of false, its selectedIndex property never returns −1, because some item is always selected. When you set editable to true and the user types a value into the TextInput portion of the control at runtime, selectedIndex returns −1 to indicate the user has provided a custom value.

Selecting complex data objects

When a List control's data provider is a collection of complex objects instead of simple values, you can refer to selected data objects' named properties using either dot syntax or array-style syntax. Dot syntax is more common, because, especially when working with classes that implement the Value Object design pattern, they allow Flex Builder and the compiler to validate property names and provide code completion.

For example, when a user selects an item that represents a complex data object from a List control, you should first cast the control's selectedItem property as the appropriate ActionScript class. You can then refer to the object's named properties and gain the benefit of Flex Builder's and the compiler's syntax checking and code completion tools:

var selectedState:StateVO = stateList.selectedItem as StateVO;
var selectedCapitol = selectedState.capitol;

If you prefer, you can use array-style syntax to refer to a data object's named properties:

var selectedCapitol = stateList.selectedItem["capitol"];

This syntax allows you to use variables containing the names of the properties. The following code would have the same functional result as the other preceding examples:

var fieldName:String = "capitol";
var selectedCapitol = stateList.selectedItem[fieldName];

Particularly when using data model classes that implement the Value Object design pattern, you may want to declare a bindable instance of the class to store the most recently selected data item. This StateVO value object class contains two properties, both of which are bindable due to the use of the [Bindable] metadata tag before the class declaration:

package vo
{
   [Bindable]
   public class StateVO
   {
     public var state:String;
     public var capitol:String;
     public function StateVO()
     {
     }
  }
}

The application in Listing 17.6 uses a ComboBox with a data provider containing multiple instances of a value object class. Upon application startup, and then again when the user selects an item from the control, a reference to the currently selected data item is saved to the selectedState variable.

Notice that this variable is marked as bindable, and its internal [Bindable] tag also marks its properties as bindable. Both levels of "bindability" are required in order for the Label controls to successfully display the selected object's properties whenever the user selects new data.

Example 17.6. Selecting complex data objects

<?xml version="1.0" encoding="utf-8"?>
 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
       layout="vertical"
xmlns:vo="vo.*" creationComplete="setSelectedState()">
 <mx:Script>
    <![CDATA[
         import vo.StateVO;
         [Bindable]
         private var selectedState:StateVO;
         private function setSelectedState():void
         {
            selectedState=stateList.selectedItem as StateVO;
        }
]]>
</mx:Script>
<mx:Array id="stateData">
    <vo:StateVO>
         <vo:state>CA</vo:state>
         <vo:capitol>Sacramento</vo:capitol>
   </vo:StateVO>
   <vo:StateVO>
        <vo:state>OR</vo:state>
        <vo:capitol>Salem</vo:capitol7gt;
   </vo:StateVO>
   <vo:StateVO>
         <vo:state>WA</vo:state>
         <vo:capitol>Washington</vo:capitol>
      </vo:StateVO>
  </mx:Array>
  <mx:ComboBox id="stateList"
       dataProvider="{stateData}"
       labelField="capitol"
       change="setSelectedState()"/>
<mx:Label text="Selected State Information:"/>
<mx:Label text="State: {selectedState.state}"/>
<mx:Label text="Capitol: {selectedState.capitol}"/>
</mx:Application>

Note

The code in Listing 17.6 is available in the Web site files as SelectingComplexObjects.mxml in the chapter17 project.

Using Custom Item Renderers

By default, List controls display simple strings in their visual items. As described previously, you can customize the string that's displayed with the control's labelField and labelFunction properties, but if you want to create a more complex display, you need to use a custom item renderer.

All List controls allow you to declare both item renderers and item editors. The differences between renderers and editors can be described as follows:

  • Item renderers primarily display information, while item editors allow the user to modify the data that's stored in the List control's data provider.

  • Item renderers display in every item of the List control regardless of the user's interactions with the control. Item editors are displayed only when the user clicks to start editing the item.

  • Item renderers also can be marked as editors. In this case, they're still displayed on every item of List control like a normal item renderer. But, like an item editor, they allow the user to modify the data in the List control's data provider.

Note

The use of custom item renderers is described in this chapter, because they can be used with all List controls. Custom item editors are described in Chapter 18 in the section about the DataGrid control.

You declare a List control's custom item renderer as a visual component that you want the control to instantiate each time it needs to render an item visually. Each of the List controls has a default item renderer class that it assigns to its itemRenderer property. The default itemRenderer class is mx.controls.listClasses.ListItemRenderer; this class is responsible for displaying simple string values as labels. When you declare a custom renderer, you override this default selection and have the freedom to create much more complex presentations.

You can declare custom item renderers in these ways:

  • Drop-in renderersare visual components that you assign to a List control without any changes to the renderer component's default property or style settings.

  • Inline renderersare components you define and nest within an MXML declaration of the List control.

  • Component renderersare separate visual components that you define as MXML components or ActionScript classes and assign to the List control's itemRenderer property in an MXML declaration. You also can assign a component renderer at runtime with ActionScript code by using the mx.core.ClassFactory class (described below).

Using drop-in item renderers

A drop-in renderer is a visual component that you assign to the List control's itemRenderer or itemEditor properties using its complete package and class name. A limited number of components implement the IDropInListItemRenderer interface, making them eligible for this use. They include:

  • Button

  • CheckBox

  • DateField

  • Image

  • Label

  • NumericStepper

  • Text

  • TextArea

  • TextInput

At runtime, for each item the List control renders, it creates an instance of the visual component you name as the renderer and passes data to the default property for that component. For example, if you use an Image component as your custom renderer, the data is passed to the control's source property. The Label, Text, TextArea, and TextInput controls have a default property of text, and each of the other controls has its own unique property.

If a List control's data provider contains String values, each containing the location of a graphic image you want to display instead of a label, you assign the itemRenderer using the fully qualified name of the component's equivalent ActionScript class:

<mx:List id="answerList" dataProvider="{answerData}"
   itemRenderer="mx.controls.Image"/>

Caution

When assigning a drop-in or a component item renderer, you must include the entire package and class name in the itemRenderer or itemEditor declaration. Including an import statement for the class you're using as the renderer does not eliminate this requirement.

The application in Listing 17.7 uses an ArrayCollection of String values, each containing the name of an image file in the project's source root. The List control's variableRowHeight property is set to true, allowing each row of the control to adjust to the image it displays.

Example 17.7. Using a drop-in item renderer

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

 <mx:ArrayCollection id="answerData">
 <mx:String>assets/yesImage.png</mx:String>
 <mx:String>assets/noImage.png</mx:String>
 <mx:String>assets/maybeImage.png</mx:String>
</mx:ArrayCollection>

<mx:List id="answerList" dataProvider="{answerData}"
   itemRenderer="mx.controls.Image"
   rowCount="{answerData.length}"
   variableRowHeight="true"
   width="80" height="140"/>

</mx:Application>

Note

The code in Listing 17.7 is available in the Web site files as DropInRenderer.mxml in the chapter17 project.

Figure 17.5 shows the resulting application. Its List control displays the images based on the values in the control's data provider.

A List control with a drop-in item renderer

Figure 17.5. A List control with a drop-in item renderer

Note

Drop-in item renderers work effectively with both single-column controls such as the List and ComboBox and with DataGridColumn components in the context of a DataGrid. Drop-in item editors can't be used very effectively in single-column controls, because with the drop-in architecture you don't have the ability to set object properties and override default behaviors. The use of drop-in item editors is described in Chapter 18.

Tip

You can use the labelFunction and labelField properties to affect the string that is passed to the drop-in renderers. For example, this function designed for use with labelFunction adds a URL path to an image reference:

private function doIt(item:Object):String
    {
       return "http://www.myUrl.com/" + item as String;
    }

Using inline renderers and editors

An inline renderer is an MXML component that you nest with the declaration of the List control. You first nest a <mx:itemRenderer> or <mx:itemEditor> child element with the List control's MXML tags, and then within that control, you nest a set of <mx:Component> tags. Finally, within the <mx:Component> tags, you nest the control or container from which you want to extend the custom component.

If the custom component you want to use as a custom renderer is extended from the VBox container, the structure of a List control's itemRenderer declaration looks like this:

<mx:List id="myList" dataProvider="{myData}">
     <mx:itemRenderer>
       <mx:Component>
           <mx:VBox>
         ... nested components ...
          </mx:VBox>
     </mx:Component>
    </mx:itemRenderer>
</mx:List>

Tip

When you create an inline item renderer, in object-oriented terms it's a local anonymous class. Local anonymous classes have the benefit of being declared within the context of their use, in this case within the List control for which it's designed. The drawback of using an anonymous class is that it can't be reused in a different context.

Tip

The <mx:Component> declaration is a compiler tag and doesn't represent a specific ActionScript class. Its purpose is to create a new component scope within an MXML file. Variables declared within the <mx:Component> tag set are local to the custom component and, unless declared public, aren't accessible to the containing application or component. Also, within the scope of the <mx:Component> tag set, the expression this refers to the current instance of the custom component and not to the application or containing component.

Every visual component in the Flex framework has a bindable data property designed for use in the custom item renderer architecture. At runtime, the List control creates an instance of the custom component for each of its items and passes the data provider's current data item to the component instance's data property.

Within the component code, you can refer to the current data item in a binding expression to use its information. In the application in Listing 17.8, the List control displays the same image as before, but this time the image location is determined in the custom item renderer by including a literal string in the Image control declaration.

Example 17.8. Using an inline renderer

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   backgroundColor="#EEEEEE">
    <mx:ArrayCollection id="answerData">
       <mx:String>yesImage.png</mx:String>
       <mx:String>noImage.png</mx:String>
       <mx:String>maybeImage.png</mx:String>
      </mx:ArrayCollection>
   <mx:List id="myList" dataProvider="{answerData}"
     rowCount="{answerData.length}"
     variableRowHeight="true"
     width="80" height="140">
  <mx:itemRenderer>
     <mx:Component>
  <mx:Image source="assets/{data}"/>
</mx:Component>
</mx:itemRenderer>
</mx:List>
</mx:Application>

Note

The code in Listing 17.8 is available in the Web site files as InlineRenderer.mxml in the chapter17 project.

Using an inline or component renderer also makes working with data providers containing complex objects easier. The List control's data property is data typed as an ActionScript Object and is compatible with any sort of data object that might be passed from the List control's data provider. For example, if the data object has an imageSource property, the custom item renderer can use that property in a binding expression to pass values to its nested visual controls:

<mx:Image source="imageLocation/{data.imageSource}"/>

In the application in Listing 17.9, the List control's data provider contains objects with value and imageSource properties. The Image component used as the custom item renderer receives its source from the data object's imageSource property through a binding expression. The Label control at the bottom of the application displays the value property of the List control's currently selected data object through a binding expression of myList.selectedItem.value.

Example 17.9. Using complex data objects in a custom item renderer

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

<mx:ArrayCollection id="answerData">
  <mx:Object>
   <mx:value>Yes</mx:value>
     <mx:imageSource>yesImage.png</mx:imageSource>
 </mx:Object>
 <mx:Object>
       <mx:value>No</mx:value>
       <mx:imageSource>noImage.png</mx:imageSource>
  </mx:Object>
  <mx:Object>
    <mx:value>Maybe</mx:value>
    <mx:imageSource>maybeImage.png</mx:imageSource>
  </mx:Object>
</mx:ArrayCollection>

<mx:List id="myList" dataProvider="{answerData}"
     rowCount="{answerData.length}"
     variableRowHeight="true"
     width="80" height="140">
     <mx:itemRenderer>
       <mx:Component>
           <mx:Image source="assets/{data.imageSource}"/>
         </mx:Component>
      </mx:itemRenderer>
    </mx:List>
<mx:Label text="{myList.selectedItem.value}"
     fontSize="14"/>


</mx:Application>

Note

The code in Listing 17.9 is available in the Web site files as InlineRendererComplexObjects.mxml in the chapter17 project.

Tip

You cannot create an empty <mx:Component> tag set; it must have a single nested child element indicating which visual component you're extending. The content of an inline component can include ActionScript code, <mx:Binding>, <mx:Model>, and <mx:State> tags, and pretty much anything else you might declare in a custom component in a separate MXML file.

Using component item renderers

A component item renderer is a separate class that can be created as either an MXML component or an ActionScript class that extends an existing visual component from the Flex framework. As with all visual components, the custom component has the same data property as was described in the previous section on inline components. At runtime, the List control creates an instance of the named component for each item it needs to render and passes the data provider's current data object to the component instance's data property.

Note

This chapter describes how to create custom components with MXML. For details of creating components in ActionScript, see the product documentation.

You create item renderers as MXML components in the same manner as any other component. If you're using Flex Builder, you can use the New MXML Component wizard to create an MXML component source code file.

As with any MXML component, its root element is the visual component that you want your custom component to extend. The objects nested within the component's root element can use the data object and its named properties (determined by the List control's data provider) to display information dynamically.

Tip

You should create custom components in subfolders of the Flex project's source root folder and give the subfolders descriptive names reflecting the use of the components they contain. For example, in the sample application described in this section, the custom components are stored in a renderers subfolder. Although it works technically to create custom components directly in the project's source root folder, this practice can create file management and application maintenance issues.

The custom component in Listing 17.10 extends the VBox container and contains an Image and a Label component. It uses its data property to set the nested object's properties through binding expressions.

Example 17.10. A custom item renderer component built with MXML

<?xml version="1.0" encoding="utf-8"?>
 <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
      horizontalAlign="center"
      verticalScrollPolicy="off"
      horizontalScrollPolicy="off">
         <mx:Image source="assets/{data.imageSource}"/>
         <mx:Label text="{data.value}"/>
     </mx:VBox>

Note

The code in Listing 17.10 is available in the Web site files as renderers/Image Renderer.mxml in the chapter17 project.

Tip

A List control commonly "squeezes" a custom renderer component's available space and causes it to generate unwanted scrollbars. In the component in Listing 17.10, the component's verticalScrollPolicy and horizontalScrollPolicy properties are set to off to suppress scrollbars that might otherwise appear.

You use the custom renderer component with the same syntax as a drop-in renderer, supplying the fully qualified name and path of the component in the List control's itemRenderer or itemEditor property:

<mx:List id="myList" dataProvider="{answerData}"
    itemRenderer="renderers.ImageRenderer"/>

Tip

When you provide the name of the custom renderer class to the List control, it is not a binding expression, and the class name isn't wrapped in braces ({}). You're providing the class definition, in a similar way to how an effect class is passed to a trigger. Instances of classes are wrapped in binding expressions; class definitions are passed solely by name without binding syntax.

The application in Listing 17.11 uses the custom component renderer to display all of each data object's values.

Example 17.11. Using a custom component renderer

<?xml version="1.0" encoding="utf-8"?>
 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
      backgroundColor="#EEEEEE">
     <mx:ArrayCollection id="answerData">
    <mx:Object>
    <mx:value>Yes</mx:value>
<mx:imageSource>yesImage.png</mx:imageSource>
</mx:Object>
<mx:Object>
   <mx:value>No</mx:value>
   <mx:imageSource>noImage.png</mx:imageSource>
  </mx:Object>
<mx:Object>
   <mx:value>Maybe</mx:value>
   <mx:imageSource>maybeImage.png</mx:imageSource>
 </mx:Object>
</mx:ArrayCollection>
<mx:List id="myList" dataProvider="{answerData}"
    itemRenderer="renderers.ImageRenderer"
    rowCount="{answerData.length}"
    variableRowHeight="true"
    width="100" height="220"/>
<mx:Label text="{myList.selectedItem.value}"
     fontSize="14"/>
</mx:Application>

Note

The code in Listing 17.11 is available in the Web site files as ComponentRenderer.mxml in the chapter17 project.

Figure 17.6 shows the completed application. Each visual item in the List displays both the Image and the Label, each populated with data from the current data object.

Using a component renderer with multiple nested visual components

Figure 17.6. Using a component renderer with multiple nested visual components

Summary

In this chapter, I described how to use the basic functions of List controls. You learned the following:

  • A List control presents data to the user and allows him make data selections with mouse or keyboard gestures.

  • All the Listcontrols use a common set of properties and events to determine their presentation and behavior.

  • The List controls include the List, ComboBox, DataGrid, TileList, HorizontalList, Tree, AdvancedDataGrid, and OLAPDataGrid controls.

  • Controls designed exclusively for use with AIR applications populate their data with information from the local client file system.

  • You handle user selections with the change event and the selectedItem and selectedIndex properties.

  • You can customize the labels presented in List control items with the labelField and labelFunction properties.

  • Custom item renderers can be used with all List controls to create a more complex visual presentation.

  • Custom item renderers can be declared using the drop-in, inline, or component architectures.

..................Content has been hidden....................

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