Flex DataGrid Selected Row Styling

Skill

Flex DataGrid Selected Row Styling

Posted in:

So in my previous tutorial I had an example on how to change a selected row's styling. At the end of that article I said there is a better way to do this, well today I am going to show that better method. This tutorial is going to to show a good method for changing just about anything on a selected row in any list based component.

In the demo application below you can see the functionality that we are building today. Really, we could have done just about anything with the selected row that we wanted. The basic idea is that we are going to build a custom item renderer to handle updating styles when the row is selected. For more info on item renderers you can read my post on Using Item Renderers. You can also grab the source for this example if you would like.

Get Adobe Flash player

To get things started here we are going to build the basic interface structure for this example. All we have is a simple DataGrid with a few columns and that is it.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   layout="absolute" width="300" height="125">
  <mx:DataGrid width="100%" height="100%">
    <mx:columns>
      <mx:DataGridColumn headerText="First Name" dataField="firstName"/>
      <mx:DataGridColumn headerText="Last Name" dataField="lastName"/>
      <mx:DataGridColumn headerText="Age" dataField="age"/>
      <mx:DataGridColumn headerText="Sex" dataField="sex"/>
    </mx:columns>
  </mx:DataGrid>
</mx:Application>

Next up, getting some dummy data into our grid. To do this I hook into the creationComplete event of the application and call my own function named init. I add a Script tag to put my function in and then inside I add a variable for the data and the function. All this can be seen below.

First my update root application tag.

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
   width="300" height="125" creationComplete="init()">

Then the Script tag.

<mx:Script>
<![CDATA[
 import mx.collections.ArrayCollection;
 
 [Bindable]
 private var peeps:ArrayCollection;

 private function init():void
 {
   peeps = new ArrayCollection();
   peeps.addItem({firstName: "Handsome", lastName: "Dude", age: 24, sex: "male"});
   peeps.addItem({firstName: "Red", lastName: "Bloke", age: 25, sex: "male"});
   peeps.addItem({firstName: "Tall", lastName: "Guy", age: 25, sex: "male"});
   peeps.addItem({firstName: "Cute", lastName: "Girl", age: 24, sex: "female"});
   peeps.addItem({firstName: "Hot", lastName: "Chick", age: 24, sex: "female"});
   peeps.addItem({firstName: "Lazy", lastName: "Man", age: 25, sex: "male"});
 }
]]>
</mx:Script>

To get the data to show up in the grid all we need now is to update the DataGrid tag and set the dataProvider property. This will be set to the variable we just created.

<mx:DataGrid dataProvider="{peeps}" width="100%" height="100%">

On to the interesting part, to actually get our selected rows to do something we need to build a custom item renderer. In this case I simply created a custom component based on a Label. Now to get this working again we override the set data function. In the set data function I set the text of the label equal to the value of the dataField property on the value passed in as the data for the renderer. I get the dataField property name from the listData property on our renderer. The start of this custom renderer follows.

<?xml version="1.0" encoding="utf-8"?>
<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml"
   width="100%" height="100%">
  <mx:Script>
   <![CDATA[
     import mx.controls.dataGridClasses.DataGridListData;
     
     override public function set data(value:Object):void
     {
       super.data = value;
       
       if(value)
       {
         var dglistData:DataGridListData = listData as DataGridListData;
         if(value[dglistData.dataField])
           this.text = value[dglistData.dataField];
       }
     }
   ]]>
 </mx:Script>
</mx:Label>

Really, all that we have done is create the basic item renderer here. In order to tell if this renderer is in the selected row we are going to check if the data for this renderer is equal to the selected row of the DataGrid that is the owner of this renderer. In order to do this we are going to hook into the creationComplete event of our custom renderer which will call the function init. Go ahead and check out the complete code for the renderer here and I will explain the rest after.

<?xml version="1.0" encoding="utf-8"?>
<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml"
   width="100%" height="100%" creationComplete="init()">
  <mx:Script>
   <![CDATA[
     import mx.events.ListEvent;
     import mx.controls.DataGrid;
     import mx.controls.dataGridClasses.DataGridListData;
     
     private var dg:DataGrid;
     
     private function init():void
     {
       dg = listData.owner as DataGrid;
       dg.addEventListener(ListEvent.CHANGE, updateSelected);
     }
     
     override public function set data(value:Object):void
     {
       super.data = value;
       
       if(value)
       {
         var dglistData:DataGridListData = listData as DataGridListData;
         if(value[dglistData.dataField])
           this.text = value[dglistData.dataField];
         updateSelected();
       }
     }
     
     private function updateSelected(e:Event = null):void
     {
       if(!data || !dg)
         return;
       
       if(dg.selectedItem == data)
       {
         //This row is selected update stuff
         setStyle("fontWeight", "bold");
       }
       else
       {
         //This row is not selected reset stuff
         setStyle("fontWeight", "normal");
       }
     }
   ]]>
 </mx:Script>
</mx:Label>

First you can see I added a variable for the owner (DataGrid), dg of the renderer. In the init function we set our grid variable equal to the owner of this renderer using the previously mentioned listData. Then we add an event listener for the change event on our owning DataGrid which will call a new function called updateSelected. The updateSelected function checks to see if this is part of the selected row and then updates the style for this renderer or could do just about anything. The last thing we need to do is call this when the data is set so we can make sure the styles are correct when scrolling and such. The main thing to remember is renderers are reused so this is why we need to check when the data is set because it can change.

Well, that takes care of this tutorial. I hope that this helps explain some of the things you can do using Item Renderers and the data they have access to. Specifically this tutorial answers a few comments in a previous tutorial. If anyone has any questions on this or anything else feel free to leave a comment.

Anonymous
02/25/2009 - 00:35

This is very useful for row style changing

reply

karthik.k
03/23/2009 - 02:23

hi

good work man
i have one problem
instead of giving color can we set image to selected row
if you can please let me know
my requirement is critical

karthik.k
cse.k.karthik@gmail.com

reply

ajitpal singh
04/07/2009 - 23:26

Can we add drag drop to the data grid. is it possible...

reply

Anonymous
05/05/2009 - 02:09

This does not Wrap text in the cell (even though word wrap is enabled for the datagrid) as the render is Label. I have used Text as the renderer to avoid this but it gives rise to two more problems :( . 1) The mouse pointer changes and 2) it only wraps for 2 lines. my data in the cell spans more than two lines

reply

The Fattest
05/05/2009 - 12:31

Well the first problem can probably be fixed by setting selectable to false and then for the second, have you set variableRowHeight on the datagrid to true? That may fix the issue you are seeing.

reply

chauhan.icse
05/06/2009 - 05:43

I want to change the BG color of selected row on some button click.
Actually I have two button
1) highligh
2)un-highligh

they highligh/un-hight selected row on click event.
Is it possible ?
- Dharmendra

chauhan.icse@gmail.com

reply

The Fattest
05/06/2009 - 12:27

Yeah sure, you can do this by setting the selectionColor style on the DataGrid when the button is pressed. Something like below.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml"
 layout="absolute"
 width="300" height="200">
  <mx:Script>
   <![CDATA[
     private function _updateSelectionColor():void
     {
       if(this.dg.getStyle("selectionColor") != 0xFF0000) {
         this.dg.setStyle("selectionColor", 0xFF0000);
       } else {
         this.dg.setStyle("selectionColor", 0xCDFFC1);
       }
     }
   ]]>
 </mx:Script>
  <mx:DataGrid id="dg" width="100%" height="100%" selectionColor="#CDFFC1">
    <mx:dataProvider>
      <mx:Array>
        <mx:Object name="chuck" age="25" />
        <mx:Object name="mike" age="26" />
        <mx:Object name="brandon" age="24" />
      </mx:Array>
    </mx:dataProvider>
  </mx:DataGrid>
  <mx:ApplicationControlBar dock="true">
    <mx:Button label="highlight" click="this._updateSelectionColor()" />    
  </mx:ApplicationControlBar>
</mx:Application>

reply

solvina@gmail.com
07/16/2009 - 04:31

Maybe I'm missing something which is Ok - I'm being suprised by Flex on daily basis.

But what about memory? In Flex documentation there is a big headline: Every addEventListener must have removeEventListener.

I'm having quite big datagrid of dynamic updated data - using this trick my Flex app starts stockpiling memory by not garbage collect my itemRenderers - because they being referenced in parent data grid.
And it is happening quite fast.

reply

The Fattest
07/17/2009 - 10:16

The item renderers should be created once and stay the entire length of the application unless the item renderer factory changes. Therefore, there shouldn't be a reason to remove the event listener. Now, of course there are exceptions where you might have a heavy application that switches between states on many different screens, in this case you would have to have a fancier solution.

Are you finding that it is recreating item renderers multiple times?

reply

solvina@gmail.com
07/20/2009 - 06:10

Hi, first I'm sorry about my terrible English.

I have no idea why my app is not reusing those custom item renderes. AFAIK it should work as you are describing. It is probably my mistake and I really have to take deep look at it.

Hm, if you are interested I would more than happy to send you snippet from my code for overview - I doubt if I can find my own mistakes :-).

So far I fallback to AdvancedDataGrid (we have full version of Flex Builder) and I'm using styleFunction property and it works nicely.

reply

Helper
11/05/2009 - 15:46

I need this functionality for multiple row selection.
How can I set multiple selected rows to bold?
I would first need an AdvancedDataGrid, of course.
Has anyone done this with multiple selection in an AdvancedDataGrid?

Thanks.

reply

Helper
11/17/2009 - 20:18

How can I gain access to the listItems array in the AdvancedListBase.as class?

I have an AdvancedDataGrid with an itemRenderer for each selected cell.

The listItems array contains the itemRenderer instances.

I need direct access to the listItems array so that I can set a style for each selected cell.

For example:

listItems[selectedRowIndex][selectedColumnIndex].setStyle("fontWeight", "bold");

reply

Anonymous
11/18/2009 - 18:15

here is the answer to my previous question:

var irList:Array = adg.mx_internal::rendererArray;

Also, read this short article:
http://raghuonflex.wordpress.com/2007/09/03/mx_internal-a-life-saver/

As an alternative to mx_internal, see this jira request:
https://bugs.adobe.com/jira/browse/FLEXDMV-2262

reply

Abhishek Flex
05/11/2010 - 06:15

hey frnd,
i want to change background color of specific rows of datagrid at run time on button click,
Is anygood solution u have?
thx in advanced,
abhishekchess1@gmail.com

reply

Helper
11/19/2009 - 13:46

Add more data to this grid and you will notice a significant slow down when highlighting a row!
How can performance be improved?

reply

AJAM
12/10/2009 - 11:33

Great work, congrats.
Any ideas on how to affect a single cell? It's easy to do once you have it, say after a click. But imagine some event outside the DG, like a button pressed. Can't find something like DG.cell[row][col].background = 0xsomecolor... unless you go and affect the whole itemrenderer?

reply

fairy.c.smoke@gmail.com
06/24/2010 - 02:50

hi,

My Datagrid is allowMultipleSelection, so , how make set the feature you have proposed to all the items selected?

reply

Tai
07/17/2010 - 02:50

Hello,
Can I insert an image before the label.

Thanks.

reply

Ravi Ranjan
08/18/2010 - 02:06

Hi,

That was indeed a very helpful article.
But in my case i have a Canvas as itemRenderer, which consist a button behaving as toogle button. Now whenever we use this mechanism for the one of the state of the button, the color of whole of the Data grid changes.

Can u help me with this problem.

Thanks,
Ravi

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors