Coloring the Background of Cells

The Flex DataGrid is probably the most commonly used control (after Labels and Buttons). One question that keeps popping up is, "How do I color the background of the cells?" I’ll answer that question plus show you how to color the background of columns and rows, too.

Click here to download the source to the samples shown.

Cell Background Color

You need an itemRenderer to change the background color of specific cells. An itemRenderer either applies to all the cells in a DataGrid (when specified in the <mx:DataGrid> tag) or all the cells in a column (when specified in the <mx:DataGridColumn> tag).

Here’s an example of coloring the background of cells in the Year column. Those with a value less than 2000 are blue and those greater than 2000 are green.

Changing the background color of a cell is as simple as overriding the updateDisplayList function and drawing a filled rectangle. If all you want to do is color the background of a cell, then you can make an itemRenderer that extends mx.controls.Label. This code is for a simple itemRenderer, based on Label, that colors the background blue if the value of year is less than 2000 and green otherwise:

<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script><![CDATA[
     override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
     {
          super.updateDisplayList(unscaledWidth,unscaledHeight);
          var g:Graphics = graphics;
          g.clear();
          g.beginFill( data.year < 2000 ? 0x0000FF : 0x00FF00 );
          g.drawRect(0,0,unscaledWidth,unscaledHeight);
          g.endFill();
     }
   ]]></mx:Script>
</mx:Label>

Column Background Color

You do not need to make an itemRenderer just to color the background of every cell in a column. The DataGridColumn class uses the backgroundColor style and applies it uniformly to a column. In fact, the background really is a background – it has nothing to do with cells as the color is applied below the cells.

<mx:DataGridColumn headerText="Make" dataField="col1" backgroundColor="red" />

The Make column will have a solid red background. Cell highlight and selection color are applied on top of the background, so those indicators will be seen. But any row colors, such as the DataGrid’s alternating row colors, will not be seen as the column’s backgroundColor is solid and above the row color. Here’s an example of coloring the columns:

Column Background Alpha

Unfortunately, the DataGridColumn does not use the backgroundAlpha style and the color is always solid. But it is pretty easy to change if you need to have some transparency to a column’s background.

The first thing you need to do is create a class that extends DataGrid. You can do this either as an MXML file (with DataGrid as its root tag) or with an ActionScript class extending DataGrid.

In your class, overrride the drawColumnBackground function and adjust the alpha level:

override protected function drawColumnBackground(s:Sprite, columnIndex:int, color:uint,
                                                                            column:DataGridColumn):void
{
      super.drawColumnBackground(s,columnIndex,color,column);

      var background:Shape = Shape(s.getChildByName(columnIndex.toString()));
      if( background ) {
          background.alpha = 0.5;
      }
}

The function calls the super class version to do all the hard work, then sets the background’s alpha to 0.5. Now the color will be semi-transparent, showing the alternating row colors, as seen in this image:

To make the class more reusable, use a public property for the alpha value. Add to your class:

public var columnBackgroundAlpha:Number = 1;

And change the function:

       backgroundAlpha = columnBackgroundAlpha;

The default behavior is to mimic the way the DataGrid works and apply a solid background color. To make the backgrounds more translucent:

<local:ColoredBackgroundDataGrid columnBackgroundAlpha="0.3" … >

Assuming you called your class ColoredBackgroundDataGrid, the above MXML tag sets the alpha to 0.3, making it very transparent. Now you have a reusable class.

Row Background Color

Next to coloring the background of specific cells, coloring the background of an entire row is probably the most popular color-related question for the DataGrid. Here’s an example of coloring the background of some rows based on the data in the row:

Setting the background color of a row is accomplished by overriding the drawRowBackground function:

override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint,
                                                                      dataIndex:int):void
{
      color = 0xFF0000;
      super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
}

In this example the row will have a solid red background, ignoring the color value passed to the function. Of course, our aim is to look at the data for the given row and determine the color. So the function changes to:

override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint,
                                                                       dataIndex:int):void
{
      var dp:ArrayCollection = dataProvider as ArrayCollection;
      var item:Object;
      if( dataIndex < dp.length ) item = dp.getItemAt(dataIndex);
      if( item != null && item.year < 20000 ) color = 0xFF8800;
      else if( item != null && item.year>= 2000 ) color = 0xFFFFFF;
      else color = 0x00CC00;
      super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
      }

Now the color is determined by the value of the year field for each row: less than 2000 is orange, greater than 2000 is white, and if there is no item for the row it is green.

The dataIndex is may be larger than the number of items in the dataProvider. That’s because you may have more rows showing than you have data. For example, if you can see 10 rows in the DataGrid but you have only 6 records in the DataProvider, 4 of those rows have no data, thus the item will be null.

The colors for this example are hard-coded and is determined by a specific data field, also hard-coded. You can make this class re-usable by giving your class a color function, which is like a labelFunction for a column, except this would return a color value instead of a String. To do this, add another public property:

public var rowColorFunction:Function;

Notice the data type is Function. You can use that in the drawRowBackground function:

override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint,
                                                                       dataIndex:int):void
{
      if( rowColorFunction != null ) {
           var dp:ArrayCollection = dataProvider as ArrayCollection;
           var item:Object;
           if( dataIndex < dp.length ) item = dp.getItemAt(dataIndex);
           color = rowColorFunction( item, rowIndex, dataIndex, color );
      }
      super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
}

Now drawRowBackground looks to see if the rowColorFunction is defined. If it is NOT defined, then the standard color is used. If the rowColorFunction is defined, then it is invoked with some parameters and it is expected to return a color.

Here’s an example with the result shown in the image above:

<local:ColoredBackgroundDataGrid rowColorFunction="determineColor" … >

private function determineColor( item:Object, rowIndex:int, dataIndex:int, oldColor:uint ) : uint
{
      if( item == null ) return 0x00CC00; // green are empty rows
      if( item.year< 2000 ) return 0xFFCC00;
      else if( item.year >= 2000 ) return 0xFFFFFF;
}

The logic used in the hard-coded drawRowFunction is now in a function in the main application. This means you can use the ColoredBackgroundDataGrid anywhere you would normally use the DataGrid.

Advanced Column Background

A function similar to the rowColorFunction can be applied to columns. Here’s one possibility:

Consider this columnBackgroundFunction:

private function columnGradient(column:DataGridColumn, columnIndex:int, columnShape:Shape,
                                               x:Number, y:Number, width:Number, height:Number) : void
{
      var m:Matrix = new Matrix();
      m.createGradientBox(width,height,0,x,0);
      columnShape.graphics.clear();
      columnShape.graphics.beginGradientFill(GradientType.LINEAR,[0xFFFF00,0xFF0000],[1,1],[0,255],m);
      columnShape.graphics.drawRect(x,y,width,height);
      columnShape.graphics.endFill();
}

This function draws a gradient-filled rectangle in the columnShape (see image above). Using a Shape allows lightweight graphics to be drawn as the background to the column; you can do the same for rows.

Change the drawColumnBackground function in your DataGrid class as follows:

override protected function drawColumnBackground(s:Sprite, columnIndex:int, color:uint,
                                                                           column:DataGridColumn):void
{
      super.drawColumnBackground(s,columnIndex,color,column);

      var background:Shape = Shape(s.getChildByName(columnIndex.toString()));
      if( background ) {
          background.alpha = columnBackgroundAlpha;
      }

      if( columnBackgroundFunction != null ) {
          var columnShape:Shape = Shape(s.getChildByName("lines"+columnIndex.toString()));
          if( columnShape == null ) {
              columnShape = new Shape();
              columnShape.name = "lines"+columnIndex;
              s.addChild(columnShape);
          }
          var lastRow:Object = rowInfo[listItems.length - 1];
          var xx:Number = listItems[0][columnIndex].x;
          var yy:Number = rowInfo[0].y;
          var ww:Number = listItems[0][columnIndex].width;
          if (this.headerHeight > 0)
          yy += rowInfo[0].height;
          var hh:Number = Math.min(lastRow.y + lastRow.height,
                                                  listContent.height – yy);
          columnBackgroundFunction( column, columnIndex, columnShape, xx, yy, ww, hh );
      }
}

As you can see, this is more complex as a Shape must be created, added to the Sprite, and location and dimension calculated.

Conclusion

While the DataGrid does provide the ability to color the background of columns, you cannot change the background alpha of the columns. You also do not need to use an itemRenderer to set the background of a row (or a column). Instead, by creating a class that extends DataGrid and overriding a couple of functions, you can easily color the backgrounds of rows or columns (or both) and make it generic enough to use the new class anywhere.

The advanced columnBackgroundFunction should give you some ideas of how advanced you can get when considering the possibilities for the background to your DataGrids.