In Flex one of the most used and useful components is the DataGrid, and it happens to be the component we get the most questions on. This tutorial's sole purpose is to show off some of the cool things that can be done with the Flex Datagrid and to answer a few of the questions that we have outstanding. Some of the topics covered in this tutorial include changing the row color, header colors, header renderers, using dynamic xml data and others.
n Flex one of the most used and useful components is the DataGrid, and it happens to be the component we get the most questions on. This tutorial's sole purpose is to show off some of the cool things that can be done with the Flex Datagrid and to answer a few of the questions that we have outstanding. Some of the topics covered in this tutorial include changing the row color, header colors, header renderers, using dynamic xml data and others.
You can check out the example below to see some of the things that are handled in this tutorial. As you can see we have a simple datagrid with 3 columns. The columns can be sorted, resized, and moved like normal. The first column |
The XML Data
First things first let's take a look at the data that we will be using for this application. The data is held in a small xml file that is on our server. It holds a list of books and data about them. Our Flex application will pull this data every time the application loads so we could update this data on the server and the next time our application would load new data would be there.
Shown below are the contents of the xml file. As you can see each book has a title, author, url, isbn 10, and read variable. The isbn 10 number is an attribute and will have to be pulled from the data slightly different from the rest of the variables. The read variable is what we are going to use to color the rows and signifies whether I have read the book or not.
<books>
<book isbn_10="1594482918">
<title>The Adventures of Johnny Bunko </title>
<author>Daniel H. Pink </author>
<url> <![CDATA[http://www.amazon.com/Adventures-Johnny-Bunko-Career-Guide/dp/1594482918/ref=pd_bbs_1?ie=UTF8&s=books&qid=1216932295&sr=8-1]]> </url>
<read>true </read>
</book>
<book isbn_10="0930289234">
<title>Watchmen </title>
<author>Alan Moore, Dave Gibbons </author>
<url> <![CDATA[http://www.amazon.com/Watchmen-Alan-Moore/dp/0930289234/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1216932775&sr=1-1]]> </url>
<read>false </read>
</book>
<book isbn_10="159184021X">
<title>Purple Cow: Transform Your Business by Being Remarkable </title>
<author>Seth Godin </author>
<url> <![CDATA[http://www.amazon.com/Purple-Cow-Transform-Business-Remarkable/dp/159184021X/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1216932833&sr=1-1]]> </url>
<read>true </read>
</book>
<book isbn_10="1847194443">
<title>Learning Drupal 6 Module Development </title>
<author>Matt Butcher </author>
<url> <![CDATA[http://www.amazon.com/Learning-Drupal-6-Module-Development/dp/1847194443/ref=pd_bbs_sr_3?ie=UTF8&s=books&qid=1216933022&sr=1-3]]> </url>
<read>true </read>
</book>
<book isbn_10="0672328917">
<title>Windows Presentation Foundation Unleashed </title>
<author>Adam Nathan </author>
<url> <![CDATA[http://www.amazon.com/Windows-Presentation-Foundation-Unleashed-WPF/dp/0672328917/ref=pd_bbs_sr_2?ie=UTF8&s=books&qid=1216933086&sr=1-2]]> </url>
<read>false </read>
</book>
</books>
Custom Row Colors
The first item for building the demo application is creating the user interface for it. This is going to be as simple as it gets. We are going to add a DataGrid and a few columns to our application. The columns are for the Title, Author, and ISBN number of the book. The start of our demo application follows.
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
layout="absolute"
width="500" height="200">
<mx:DataGrid id="booksGrid" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="title" headerText="Title"/>
<mx:DataGridColumn dataField="author" headerText="Author"/>
<mx:DataGridColumn headerText="ISBN 10"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>
To get things moving we are going to create a custom component that extends DataGrid to handle changing the row color. I am going to be using a modified method created by (or at least that is where I saw it) Peter Ent. The method uses the protected
function, drawRowBackground
, in the DataGrid class. Which is the function that handles drawing the row backgrounds, of course. To start with we create the component, in my case named RowColorDataGrid
.
{
import flash. display. Sprite;
import mx. collections. ArrayCollection;
import mx. controls. DataGrid;
public class RowColorDataGrid extends DataGrid
{
public var rowColorFunction: Function;
override protected function drawRowBackground (
s:Sprite, rowIndex: int, y: Number, height: Number,
color:uint, dataIndex: int ): void
{
super. drawRowBackground (
s, rowIndex, y, height, color, dataIndex );
}
}
}
In order to make changing the row color easy we are going to use a pattern that is used throughout the Flex framework, a callback function to determine the row color - similar to a labelFunction. You can see the public variable, of type Function, in the class declaration above called rowColorFunction
. The function will have a few parameters passed and returns the color of the background. We need to add a little more code to our overridden function to use the rowColorFunction
to determine the wanted color. Here is the function and I will explain the additions right after.
s:Sprite, rowIndex: int, y: Number, height: Number,
color:uint, dataIndex: int ): void
{
if (rowColorFunction != null )
{
var item: Object;
if (dataIndex <dataProvider. length )
{
item = dataProvider [dataIndex ];
}
if (item )
{
color =
rowColorFunction (item, rowIndex, dataIndex, color );
}
}
super. drawRowBackground (
s, rowIndex, y, height, color, dataIndex );
}
First thing in the function we check to make sure the rowColorFunction
is set, otherwise we default to the original implementation by the DataGrid
class. If one is set we grab the item for the row that is being drawn from the dataProvider
. I modified this from Peter Ent's method to handle all the types the dataProvider
can be. After grabbing the item I check to make sure it isn't null and call the row color function. The rest is handled by the drawRowBackground
call.
Now we need to update the application to use our custom component. Below we have the updated grid code.
id="booksGrid"
dataProvider="{books}" width="100%" height="100%"
rowColorFunction="calcRowColor">
<local:columns>
<mx:DataGridColumn dataField="title" headerText="Title"/>
<mx:DataGridColumn dataField="author" headerText="Author"
width="125" />
<mx:DataGridColumn headerText="ISBN 10" width="90"/>
</local:columns>
</local:RowColorDataGrid>
You can notice I have the rowColorFunction
property set and also have set the dataProvider
property. A Script
tag is added and we add the function and an XMLListCollection for the holding our data.
<![CDATA[
import mx.collections.XMLListCollection;
[Bindable]
private var books:XMLListCollection;
private function calcRowColor(
item:Object, rowIndex:int,
dataIndex:int, color:uint):uint
{
if(item.read == true)
return 0x49FFAD;
else
return color;
}
]]>
</mx:Script>
Our function, calcRowColor
, simply checks if the item passed in has the read
property set equal to true. If the read
property is not true, the function simply returns the default background color. There are endless possibilities of what can be done with this so go and have fun.
Getting XML Data with HTTPService
This is going to be one of the easiest features we are going to add to the application today. Foremost, we are going to add a HTTPService to our application. We are going to set a few attributes on the tag. These include the id
, url
, result
, and resultFormat
. The url
is pointed the books xml file from earlier in the post. The resultFormat
is set to e4x
to make handling the xml data all nice and easy. Following we have the added tag.
resultFormat="e4x"
url="/files/Tutorials/Flex/DatagridOptionsOne/books.xml" />
Now we have something to use to get our data we actually need to handle the result and send for it. To do this we are going to hook into the creationComplete
event on the main tag in our application. You can also see above we have already hooked into the result
event of the HTTPService
tag. This means we just need to implement the function. But first we have our updated Application
tag.
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
layout="absolute"
width="500" height="200"
creationComplete="init()">
Furthermore, we have the two function for sending to get the xml data and the handleData
function to process the result.
{
booksService. send ( );
}
private function handleData (event:ResultEvent ): void
{
books = new XMLListCollection ( );
books. source = event. result. book;
}
Both of those functions above are pretty self-explanatory but I will give a quick rundown. First the send
function on the http service object will send the request. The processing done is simple, we create a new collection to hold our books then pull out the book list from the result.
DataGrid Item Presentation
Up to this point you should be able to run the application and see the datagrid showing some data with a couple rows that have a greenish background marking they have been read. To add to the mix we have one column that needs a label function to display the data correctly. This is the ISBN column because the ISBN 10 number is passed as an xml attribute which can pulled out using the e4x "@" syntax. We update our column definition to set the labelFunction
property and then add the label function to our actionscript.
headerText="ISBN 10" width="90"/>
The label function.
getISBN (item: Object, col:DataGridColumn ): String
{
return item.@isbn_10;
}
That should make the ISBN 10 number show up in the grid correctly. The next task is to build a item renderer for the title so we can add a link to the actual amazon page of the book. It will use the url
property passed through with the book data in the xml. Before starting on the actual renderer I am going to show a helper component that I built to link to external sites. The component extends the LinkButton class. So without further ado.
{
import flash. events. MouseEvent;
import flash. net. URLRequest;
import flash. net. navigateToURL;
import mx. controls. LinkButton;
public class ExternalLinkButton extends LinkButton
{
public var url: String = "";
public function ExternalLinkButton ( )
{
super ( );
addEventListener (MouseEvent. CLICK, followLink );
}
private function followLink (event:MouseEvent ): void
{
if ( url == "" )
return;
var urlReq:URLRequest = new URLRequest ( url );
navigateToURL (urlReq );
}
}
}
It is very understandable code so I am not going to go over it - feel free to ask any questions on it in the comments though. Next we are going to build the title item renderer, which is almost as easy so I am going to throw it out there and then go over the code.
<mx:HBox
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
width="100%" height="100%"
horizontalScrollPolicy="off"
horizontalGap="0">
<mx:Label text="{data.title}"/>
<local:ExternalLinkButton
icon="@Embed('/assets/link.gif')" url="{data.url}"
width="22" height="100%" textAlign="left"/>
</mx:HBox>
As you can see in the code above we have a label which has the text set to the title of the book and a ExternalLinkButton
. The button has an icon set and the url set to the Amazon page, along with a couple other properties. All this is put in a HBox
to make layout easy. I would like to note that you could have easy set the label on the link button to the title and simply had a big link. I choose the icon route to make it easy to select a row of the data which being sent to Amazon. This pretty much takes care of showing the data in the grid.
Customizing the DataGridColumn Headers
The final couple pieces of this tutorial are going to deal with the column headers and how we can customize them a little. First we are going to hook into one of the events, headerRelease
, of the DataGrid class and in our case the custom component we built. This event is fired after the one of the headers is clicked on. We are going to use this to update the style of the header clicked on. This gives the final iteration of our RowColorDataGrid
tag.
id="booksGrid"
dataProvider="{books}" width="100%" height="100%"
headerRelease="{headerColor(event)}"
rowColorFunction="calcRowColor">
We now add the headerColor
function to our script which will look like the following.
{
if (event. columnIndex != 0 )
return;
var col:DataGridColumn =
booksGrid. columns [event. columnIndex ];
var headerStyle: String = col. getStyle ( "headerStyleName" );
if (headerStyle != "headerBlue" )
col. setStyle ( "headerStyleName", "headerBlue" );
else
col. setStyle ( "headerStyleName", "headerRed" );
}
In the above function we start off by checking to see if the column we are in is the title column (index 0) and if not we return out of the function. Then we declare a local variable, col
, for the column that we clicked on. We use this to get the current style name (headerStyleName
) for the column header. Then we check to see if it is set to "headerBlue" and if it isn't we set it and otherwise set it to "headerRed". Both "headerBlue" and "headerRed" are style classes that we declare in a Style
tag of our application. This style tag is below.
RowColorDataGrid { use-roll-over: false; }
ExternalLinkButton { roll-over-color: #FF5BBA ; }
.headerRed { color: #FF0000; }
.headerBlue { color: #0000FF; }
</mx:Style>
The final thing we are going to do to our data grid is use a custom header renderer for the author column. The single purpose of this header renderer is going to be update the text color when we hover over the header, as opposed to updating on click like the previous method. For the renderer we are going to build yet another component. This component named LabelHeader
is based off of the Box
component and has a single label in it with the text set to normal headerText
property of the column. Check out the code below for the entire component definition.
<mx:Box
xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%" height="100%"
mouseOver="mouseOverHandler(event)"
mouseOut="mouseOutHandler(event)">
<mx:Script>
<![CDATA[
private function mouseOverHandler(event:MouseEvent):void
{
headerLabel.setStyle("color", 0xFF0000);
}
private function mouseOutHandler(event:MouseEvent):void
{
headerLabel.setStyle("color", 0x000000);
}
]]>
</mx:Script>
<mx:Label
id="headerLabel"
width="100%" height="100%"
text="{data.headerText}" />
</mx:Box>
We hook into two events, mouseOver
and mouseOut
. The handlers for these are simple as pi - they simply update the text color of the label. On the DataGridColumn
tag we set the headerRenderer
property to LabelHeader
and voilà.
width="125" headerRenderer="LabelHeader" />
Just to satisfy the curious here is the entire main application file contents.
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
layout="absolute"
width="500" height="200"
creationComplete="init()">
<mx:Style>
RowColorDataGrid { use-roll-over: false; }
ExternalLinkButton { roll-over-color: #FF5BBA ; }
.headerRed { color: #FF0000; }
.headerBlue { color: #0000FF; }
</mx:Style>
<mx:Script>
<![CDATA[
import mx.utils.ColorUtil;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.rpc.events.ResultEvent;
import mx.collections.XMLListCollection;
import mx.events.DataGridEvent;
[Bindable]
private var books:XMLListCollection;
private function init():void
{
booksService.send();
}
private function handleData(event:ResultEvent):void
{
books = new XMLListCollection();
books.source = event.result.book;
}
private function headerColor(event:DataGridEvent):void
{
if(event.columnIndex != 0)
return;
var col:DataGridColumn =
booksGrid.columns[event.columnIndex];
var headerStyle:String = col.getStyle("headerStyleName");
if(headerStyle != "headerBlue")
col.setStyle("headerStyleName", "headerBlue");
else
col.setStyle("headerStyleName", "headerRed");
}
private function
getISBN(item:Object, col:DataGridColumn):String
{
return item.@isbn_10;
}
private function calcRowColor(
item:Object, rowIndex:int,
dataIndex:int, color:uint):uint
{
if(item.read == true)
return 0x49FFAD;
else
return color;
}
]]>
</mx:Script>
<mx:HTTPService id="booksService" result="handleData(event)"
resultFormat="e4x"
url="/files/Tutorials/Flex/DatagridOptionsOne/books.xml" />
<local:RowColorDataGrid
id="booksGrid"
dataProvider="{books}" width="100%" height="100%"
headerRelease="{headerColor(event)}"
rowColorFunction="calcRowColor">
<local:columns>
<mx:DataGridColumn dataField="title" headerText="Title"
headerStyleName="headerRed"
itemRenderer="BookTitleRenderer"/>
<mx:DataGridColumn dataField="author" headerText="Author"
width="125" headerRenderer="LabelHeader" />
<mx:DataGridColumn labelFunction="getISBN"
headerText="ISBN 10" width="90"/>
</local:columns>
</local:RowColorDataGrid>
</mx:Application>
Well I think I answered a few questions people had about the datagrid. If anyone has any other questions feel free to email us or drop a comment. Hope everyone was able to take away something from this tutorial.