Flex DataGrid Goodies - Row Color and Others

http://blog.paranoidferret.com/index.php/2008/07/30/flex-datagrid-goodies-row-color-and-others/

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 Title has a header that starts off red and flips to blue when clicked, and the contents of the first column have external link buttons to send you off to the site relevant to that row. The second column has a header that changes color when hovered. And one of the most noticeable attributes is the row color for some of the pieces of data, the row color for these is determined by whether the item has been read - a property in the data. Now take a second and mess around with the example or grab the source code.


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.

<?xml version="1.0" encoding="UTF-8"?>
<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.

<?xml version="1.0" encoding="utf-8"?>
<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.

package
{
  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.

override protected function drawRowBackground (
  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.

<local:RowColorDataGrid
  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.

<mx:Script>
<![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.

<mx:HTTPService id="booksService" result="handleData(event)"
  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.

<mx:Application
  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.

private function init ( ): void
{
  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.

<mx:DataGridColumn labelFunction="getISBN"
  headerText="ISBN 10" width="90"/>

The label function.

private 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.

package
{
  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.

<?xml version="1.0" encoding="utf-8"?>
<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.

<local:RowColorDataGrid
  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.

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" );
}

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.

<mx:Style>
  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.

<?xml version="1.0" encoding="utf-8"?>
<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à.

<mx:DataGridColumn dataField="author" headerText="Author"
  width="125" headerRenderer="LabelHeader" />

Just to satisfy the curious here is the entire main application file contents.

<?xml version="1.0" encoding="utf-8"?>
<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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值