15 October 2008
General experience with programming in ActionScript 3 is suggested.
Intermediate
This is the third of three quick start tutorials that explain best practices for working with the ActionScript 3.0 DataGrid component. The DataGrid component lets you easily filter data based on user input and present both the data and the data grid with your custom formatting.
This article shows you how to do the following tasks with data grids: filter items in a data provider, align text in data grid columns, alternate background colors in a data grid, set background color based on a row's data, and embed components into data grid cells.
Note: The following examples require that a DataGrid component exists in your document's library.
When building applications you'll want to give the user a quick, easy way to filter data. For example, if you had a ComboBox, List, or DataGrid with several hundred (or even thousand) records, it could be very frustrating for users to scroll through a few hundred results to find a specific page or user name. If users are able to filter data quickly by typing the first few letters of a string, the results narrow down considerably.
Note: The following example requires both a DataGrid and TextInput component in the library.
The following example displays a few different vegetables and allows users to find a specific vegetable by typing the first letter or two. The example filters items in a data provider based on the text in a TextInput component:
// Import the required component classes.
import fl.controls.DataGrid;
import fl.controls.TextInput;
import fl.controls.dataGridClasses.DataGridColumn;
import fl.data.DataProvider;
// Create and populate a new DataProvider object.
var dp:DataProvider = new DataProvider();
dp.addItem({item:"Asparagus", price:0.53});
dp.addItem({item:"Brussel Sprouts", price:0.27});
dp.addItem({item:"Cabbage", price:0.04});
dp.addItem({item:"Cauliflower", price:0.16});
// Create a new TextInput component instance and add it to the display list.
var itemTextInput:TextInput = new TextInput();
itemTextInput.move(10, 10);
itemTextInput.addEventListener(Event.CHANGE, changeHandler);
addChild(itemTextInput);
// Create a new DataGridColumn object.
var itemCol:DataGridColumn = new DataGridColumn("item");
itemCol.headerText = "Vegetable:";
/* Create a new DataGridColumn object, and assign a label function
and sort options. */
var priceCol:DataGridColumn = new DataGridColumn("price");
priceCol.headerText = "Price (per/lb):";
priceCol.labelFunction = priceLabelFunction;
priceCol.sortOptions = Array.NUMERIC;
/* Create a new DataGrid component instance, add the two DataGridColumn
objects created earlier, define the data provider and add the instance
to the display list. */
var myDataGrid:DataGrid = new DataGrid();
myDataGrid.addColumn(itemCol);
myDataGrid.addColumn(priceCol);
myDataGrid.dataProvider = dp;
myDataGrid.width = 200;
myDataGrid.rowCount = myDataGrid.length;
myDataGrid.move(10, 40);
addChild(myDataGrid);
/* This function is used by the priceCol object's labelFunction property. This function
formats each row of the associated data grid column (priceCol) and returns a currency
formatted string. */
function priceLabelFunction(item:Object):String {
return "$" + Number(item.price).toFixed(2);
}
/* Handler function for the TextInput component instance. This function converts the
data provider (dp) to an array using the DataProvider class's toArray() method, and
then filters the newly created array using the Array class's filter() method. Finally,
the data grid's data provider property is set to the contents of the filtered array. */
function changeHandler(event:Event):void {
var arr:Array = dp.toArray();
var filteredArr:Array = arr.filter(filterDataProvider);
myDataGrid.dataProvider = new DataProvider(filteredArr);
}
/* This function is called by the changeHandler() function and is used to filter the
contents of an array. This function takes the current contents of the TextInput
component instance and compares the contents against the current item in the array.
If the strings match, the filterDataProvider() method returns true and the current
item is added to the new array. If the strings do not match, the method returns
false and the item is not added. */
function filterDataProvider(obj:Object, idx:int, arr:Array):Boolean {
var txt1:String = itemTextInput.text;
var txt2:String = obj.item.substr(0, txt1.length);
if (txt1.toLowerCase() == txt2.toLowerCase()) {
return true;
}
return false;
}
To get the source files for this example, download section10_example1.zip from the top of this page.
You may have noticed in the previous example that even though the price is numeric, the column was aligned left (default). Often when displaying numbers in an application, you may want to align text right (or center) to make it easier to read.
The following example lets you right-align values in a column by using a custom cell renderer. It formats the contents of a column by using a custom label function (dollarLabelFunction()), and then aligns the contents by setting the data grid column's cellRenderer property to a custom cell renderer (RightAlignCell):
// Import the required component classes.
import fl.controls.DataGrid;
import fl.controls.dataGridClasses.DataGridColumn;
import fl.data.DataProvider;
// Create and populate a new DataProvider object.
var dp:DataProvider = new DataProvider();
dp.addItem({name:"Item 1", price:1234.00});
dp.addItem({name:"Item 2", price:56.30});
dp.addItem({name:"Item 3", price:789.12});
// Create a new DataGridColumn object.
var nameCol:DataGridColumn = new DataGridColumn("name");
nameCol.headerText = "Name:";
/* Create a new DataGridColumn object, assign a label function
and sort options, and specify a custom cell renderer. */
var priceCol:DataGridColumn = new DataGridColumn("price");
priceCol.headerText = "Price (USD):";
priceCol.labelFunction = dollarLabelFunction;
priceCol.sortOptions = Array.NUMERIC;
priceCol.cellRenderer = RightAlignCell;
/* Create a new DataGrid component instance, add the two DataGridColumn
objects created earlier, define the data provider and add the instance
to the display list. */
var myDataGrid:DataGrid = new DataGrid();
myDataGrid.addColumn(nameCol);
myDataGrid.addColumn(priceCol);
myDataGrid.dataProvider = dp;
myDataGrid.width = 200;
myDataGrid.rowCount = myDataGrid.length;
myDataGrid.move(10, 10);
addChild(myDataGrid);
/* This function is used by the priceCol object's labelFunction property. This function
formats each row of the associated data grid column (priceCol) and returns a currency
formatted string. */
function dollarLabelFunction(item:Object):String {
return "$" + Number(item.price).toFixed(2);
}
The code for the RightAlignCell class is as follows:
package {
/* Import the required classes. Note that since this is an external .AS file and
not an .FLA file, most classes will not be automatically imported for you. */
import fl.controls.listClasses.CellRenderer;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
/**
* The main class definition. Make sure the class is marked "public" and in the case
* of our custom cell renderer, extends the CellRenderer class and implements the
* ICellRenderer interface.
*/
public class RightAlignCell extends CellRenderer implements ICellRenderer {
// Create a new private variable to hold the custom text format.
private var tf:TextFormat;
/**
* This method defines the TextFormat object and sets the align
* property to "right".
*/
public function RightAlignCell() {
tf = new TextFormat();
tf.align = TextFormatAlign.RIGHT;
}
/**
* Override the inherited drawLayout() method from the CellRenderer class.
* This method sets the text field's width to the width of the data grid column,
* applies the new text format using the setTextFormat() method, and calls the
* parent class's drawLayout() method.
*/
override protected function drawLayout():void {
textField.width = this.width;
textField.setTextFormat(tf);
super.drawLayout();
}
}
}
The custom RightAlignCell class extends the CellRenderer class (fl.controls.listClasses.CellRenderer) and creates a new TextFormat object in the RightAlignCell class's constructor method. Next, the CellRenderer class's drawLayout() method is overridden, and the custom TextFormat object is applied to the cell's internal TextField object using the setTextFormat() method.
To get the source files for this example, download section11_example1.zip from the top of this page.
When displaying large amounts of data in a data grid, it's easier to see the rows if they have alternating row colors. You need to create two new symbols in the Flash document's library for this sample to work. The easiest way to do this is to duplicate an existing cell renderer skin in the library (such as CellRenderer_upSkin in the Library panel's Component Assets/CellRendererSkins folder, see Figure 1) and modify the asset as desired. Once you've finished creating the desired skins for the alternating row color backgrounds, you'll need to set up a linkage and give the symbol a class name of "CellRenderer_upSkinGray" and "CellRenderer_upskinDarkGray."
The following example creates ten items in a data grid and alternates the data grid row's background colors based on the row's current index:
// Import the required component classes.
import fl.controls.DataGrid;
import fl.data.DataProvider;
// Create and populate a new DataProvider object.
var dp:DataProvider = new DataProvider();
dp.addItem({label:"Item a", data:0});
dp.addItem({label:"Item b", data:1});
dp.addItem({label:"Item c", data:2});
dp.addItem({label:"Item d", data:3});
dp.addItem({label:"Item e", data:4});
dp.addItem({label:"Item f", data:5});
dp.addItem({label:"Item g", data:6});
dp.addItem({label:"Item h", data:7});
dp.addItem({label:"Item i", data:8});
dp.addItem({label:"Item j", data:9});
/* Create a new DataGrid component instance, add two data grid columns,
define the data provider and add the instance to the display list. This
code also sets the cellRenderer style on the data grid by using the
setStyle() method and specifies the custom cell renderer class to use. */
var myDataGrid:DataGrid = new DataGrid();
myDataGrid.addColumn("label");
myDataGrid.addColumn("data");
myDataGrid.dataProvider = dp;
myDataGrid.width = 200;
myDataGrid.rowCount = dp.length;
myDataGrid.move(10, 10);
myDataGrid.setStyle("cellRenderer", AlternatingRowColors);
addChild(myDataGrid);
The AlternatingRowColors class is as follows:
package {
// Import the required component classes.
import fl.controls.listClasses.CellRenderer;
import fl.controls.listClasses.ICellRenderer;
/**
* This class sets the upSkin style based on the current item's index in a list.
* Make sure the class is marked "public" and in the case of our custom cell renderer,
* extends the CellRenderer class and implements the ICellRenderer interface.
*/
public class AlternatingRowColors extends CellRenderer implements ICellRenderer {
/**
* Constructor.
*/
public function AlternatingRowColors():void {
super();
}
/**
* This method returns the style definition object from the CellRenderer class.
*/
public static function getStyleDefinition():Object {
return CellRenderer.getStyleDefinition();
}
/**
* This method overrides the inherited drawBackground() method and sets the renderer's
* upSkin style based on the row's current index. For example, if the row index is an
* odd number, the upSkin style is set to the CellRenderer_upSkinDarkGray linkage in the
* library. If the row index is an even number, the upSkin style is set to the
* CellRenderer_upSkinGray linkage in the library.
*/
override protected function drawBackground():void {
if (_listData.index % 2 == 0) {
setStyle("upSkin", CellRenderer_upSkinGray);
} else {
setStyle("upSkin", CellRenderer_upSkinDarkGray);
}
super.drawBackground();
}
}
}
Save the previous code in the same directory as your Flash document and give it a filename of AlternatingRowColors.as.
The custom AlternatingRowColors class extends the CellRenderer class and implements the ICellRenderer interface. This class overrides the drawBackground() method from the CellRenderer class and determines which upSkin to use for the current data grid row's background based on the current row's index. If the row index is an even number, the data grid sets the upSkin style to CellRender_upSkinGray, otherwise the CellRenderer_upSkinDarkGray is used.
Note: The alternating background skins are applied using the setStyle() method in the drawBackground() method in the custom cell renderer above. For this example to work, you need to create two new skins in your library and make sure they are exported for ActionScript: CellRenderer_upSkinGray and CellRenderer_upSkinDarkGray.
To get the source files for this example, download section12_example1.zip from the top of this page.
When customizing the layout of a data grid, you may not always want row colors to be alternated based on their row index. In some cases, you may want to set a row's background color based on the value of a field or on a certain set of criteria.
The following example first creates a DataGrid component instance and then alternates row colors based on the value of the data provider's rowColor property.
Note: The alternating background skins are applied using the setStyle() method in the drawBackground() method in the CustomRowColors class below. For this example to work, you need to create two new skins in your library and make sure they are exported for ActionScript: CellRenderer_upSkinGreen and CellRenderer_upSkinRed. For more information, see the previous example, Alternating background colors in a data grid.
// Import the required component classes.
import fl.controls.DataGrid;
import fl.data.DataProvider;
// Create and populate a new DataProvider object.
var dp:DataProvider = new DataProvider();
dp.addItem({label:"Item a", data:0, rowColor:"green"});
dp.addItem({label:"Item b", data:1, rowColor:"green"});
dp.addItem({label:"Item c", data:2, rowColor:"green"});
dp.addItem({label:"Item d", data:3, rowColor:"green"});
dp.addItem({label:"Item e", data:4, rowColor:"red"});
dp.addItem({label:"Item f", data:5, rowColor:"red"});
dp.addItem({label:"Item g", data:6, rowColor:"green"});
dp.addItem({label:"Item h", data:7, rowColor:"red"});
dp.addItem({label:"Item i", data:8, rowColor:"green"});
dp.addItem({label:"Item j", data:9, rowColor:"green"});
/* Create a new DataGrid component instance, add two data grid columns,
define the data provider and add the instance to the display list. This
code also sets the cellRenderer style on the data grid by using the
setStyle() method and specifies the custom cell renderer class to use. */
var myDataGrid:DataGrid = new DataGrid();
myDataGrid.addColumn("label");
myDataGrid.addColumn("data");
myDataGrid.dataProvider = dp;
myDataGrid.width = 200;
myDataGrid.rowCount = dp.length;
myDataGrid.move(10, 10);
myDataGrid.setStyle("cellRenderer", CustomRowColors);
addChild(myDataGrid);
The CustomRowColors class is as follows:
package {
// Import the required component classes.
import fl.controls.listClasses.CellRenderer;
import fl.controls.listClasses.ICellRenderer;
/**
* This class sets the upSkin style based on the current item's rowColor value
* in the data provider.
* Make sure the class is marked "public" and in the case of our custom cell renderer,
* extends the CellRenderer class and implements the ICellRenderer interface.
*/
public class CustomRowColors extends CellRenderer implements ICellRenderer {
/**
* Constructor.
*/
public function CustomRowColors():void {
super();
}
/**
* This method returns the style definition object from the CellRenderer class.
*/
public static function getStyleDefinition():Object {
return CellRenderer.getStyleDefinition();
}
/**
* This method overrides the inherited drawBackground() method and sets the renderer's
* upSkin style based on the row's rowColor value in the data provider. For example,
* if the item's rowColor value is "green," the upSkin style is set to the
* CellRenderer_upSkinGreen linkage in the library. If the rowColor value is "red," the
* upSkin style is set to the CellRenderer_upSkinRed linkage in the library.
*/
override protected function drawBackground():void {
switch (data.rowColor) {
case "green" :
setStyle("upSkin", CellRenderer_upSkinGreen);
break;
case "red" :
setStyle("upSkin", CellRenderer_upSkinRed);
break;
default :
break;
}
super.drawBackground();
}
}
}
The drawBackground() method above checks the value of the current row's rowColor property and, depending on the value, selects either the CellRenderer_upSkinGreen or CellRenderer_upSkinRed asset from the library.
To select a skin style based on the value of the row's data property, use code similar to the following:
override protected function drawBackground():void {
/* If the data property in the data provider is less than 4, the upSkin style is
set to the CellRenderer_upSkinRed linkage in the library. */
if (data.data < 4) {
setStyle("upSkin", CellRenderer_upSkinRed);
}
super.drawBackground();
}
If the value of the row's data property is less than 4, the row's upSkin will be set to CellRenderer_upSkinRed; otherwise, the default skin will be used.
To get the source files for this example, download section13_example1.zip from the top of this page.
Often when building applications you may want to embed other component instances into a data grid. For example, you could embed a check box or combo box component instance into a cell so that a user is forced to select options from a predefined set of values instead of typing a value into an editable cell. You may also want to embed a UILoader component instance in a cell to display an image or library symbol—useful to show a book's cover image, a flag, or just a series of thumbnail images with their file names and file properties.
Before testing this example, you need to have two movie clip assets in your Flash document's library and give them class names Bear and Lion, respectively (see Figure 3).
The following example demonstrates how you can create a custom cell renderer for a data grid column which will load external images or images from the Flash document's library.
The following example creates a DataGrid component instance which loads three external JPEG images, as well as two assets from the Flash document's library. Next, the cellRenderer property of a DataGridColumn object is set to the custom cell renderer class, LoaderCellRenderer:
// Import the required component classes.
import fl.controls.DataGrid;
import fl.controls.dataGridClasses.DataGridColumn;
import fl.data.DataProvider;
/* Create and populate a new DataProvider object. Note that three of the items
in the data provider refer to externally-loaded images, whereas the last two
items refer to symbol linkages in the library. */
var dp:DataProvider = new DataProvider();
dp.addItem({data:"http://www.helpexamples.com/flash/images/image1.jpg", title:"image1.jpg"});
dp.addItem({data:"http://www.helpexamples.com/flash/images/image2.jpg", title:"image2.jpg"});
dp.addItem({data:"http://www.helpexamples.com/flash/images/image3.jpg", title:"image3.jpg"});
dp.addItem({data:"Bear", title:"Bear.jpg"});
dp.addItem({data:"Lion", title:"Lion.jpg"});
// Create a new DataGridColumn object, and specify a custom cell renderer.
var dataCol:DataGridColumn = new DataGridColumn("data");
dataCol.cellRenderer = LoaderCellRenderer;
// Create a new DataGridColumn object.
var titleCol:DataGridColumn = new DataGridColumn("title");
/* Create a new DataGrid component instance, add the two DataGridColumn
objects created earlier, define the data provider and add the instance
to the display list. */
var myDataGrid:DataGrid = new DataGrid();
myDataGrid.addColumn(dataCol);
myDataGrid.addColumn(titleCol);
myDataGrid.dataProvider = dp;
myDataGrid.rowHeight = 64;
myDataGrid.width = 200;
myDataGrid.rowCount = dp.length - 1;
myDataGrid.move(10, 10);
addChild(myDataGrid);
The LoaderCellRenderer class is as follows:
package {
// Import the required component classes.
import fl.containers.UILoader;
import fl.controls.listClasses.ICellRenderer;
import fl.controls.listClasses.ListData;
import fl.core.InvalidationType;
import fl.data.DataProvider;
import flash.events.Event;
/**
* This class creates a custom cell renderer which displays an image in a cell.
* Make sure the class is marked "public" and in the case of our custom cell renderer,
* extends the UILoader class and implements the ICellRenderer interface.
*/
public class LoaderCellRenderer extends UILoader implements ICellRenderer {
protected var _data:Object;
protected var _listData:ListData;
protected var _selected:Boolean;
/**
* Constructor.
*/
public function LoaderCellRenderer():void {
super();
}
/**
* Gets or sets the cell's internal _data property.
*/
public function get data():Object {
return _data;
}
/**
* @private (setter)
*/
public function set data(value:Object):void {
_data = value;
source = value.data;
}
/**
* Gets or sets the cell's internal _listData property.
*/
public function get listData():ListData {
return _listData;
}
/**
* @private (setter)
*/
public function set listData(value:ListData):void {
_listData = value;
invalidate(InvalidationType.DATA);
invalidate(InvalidationType.STATE);
}
/**
* Gets or sets the cell's internal _selected property.
*/
public function get selected():Boolean {
return _selected;
}
/**
* @private (setter)
*/
public function set selected(value:Boolean):void {
_selected = value;
invalidate(InvalidationType.STATE);
}
/**
* Sets the internal mouse state.
*/
public function setMouseState(state:String):void {
}
}
}
To get the source files for this example, download section14_example1.zip from the top of this page.
For further information about this topic, see Creating, populating, and resizing the DataGrid component (Part 1 of 3) and Customizing and sorting the DataGrid component (Part 2 of 3). For more information about the ActionScript 3.0 DataGrid component: