Not yet rated

Problem

Checkbox in header renderer in Datagrid to select all/Unselect all. Although there are many examples to do this there are none that address the issue of an undecided state when there is one or more selections (but not all) in the datagrid.

Solution

We can use a 3 state checkbox in header renderer that represents select, unselect and undecided states.

Detailed explanation

The headerRenderer consists of three states:
 
  • select: This would mean that all the rows are selected
  • unselect: This would mean that none of the rows are selected
  • undecided: This would mean that one or more (but not all) of the rows are selected

The default state of the checkbox is set to 'unselect'. We overide the function updateDisplayFunction() to draw the checkbox according to state. The state is determined by calling areAllSelected() and isAnyColumnSelected() methods. 

CODE:

Code for Main.mxml:

<?xml version="1.0" encoding="utf-8"?>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
xmlns:s="library://ns.adobe.com/flex/spark" 
xmlns:mx="library://ns.adobe.com/flex/mx" 
creationComplete="creationCompleteHandler(event)">

<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;

private var _dataProvider:ArrayCollection;
[Bindable]
public function set dataProvider(value:Object):void{
_dataProvider=value as ArrayCollection
}
public function get dataProvider():Object{
return _dataProvider;
}

protected function creationCompleteHandler(event:FlexEvent):void
{
dataProvider = new ArrayCollection([
{checked:false, name:'Jim Smith'},
{checked:false, name:'Yancy Williams'},
{checked:false, name:'Anne Davis'},
{checked:false, name:'Robert Miller'},
{checked:false, name:'Cathy Taylor'},
{checked:false, name:'Tom Harris'}]); 
}

]]>
</fx:Script>

<mx:DataGrid id="myDataGrid" left="10" top="10" dataProvider="@{dataProvider}">
<mx:columns>
<mx:DataGridColumn textAlign="center" dataField="checked" 
  width="90" 
  rendererIsEditor="true"
  sortable="false"
  itemRenderer="renderer.CheckboxItemRenderer"
  headerRenderer="renderer.ThreeStateCheckBoxHeaderRenderer"
  />
<mx:DataGridColumn headerText="Name" dataField="name" width="110"/>
</mx:columns>
</mx:DataGrid>
 
</s:Application>

Here is the code for HeaderRenderer that does the main work:

        package renderer
{
import flash.events.MouseEvent;

import mx.collections.ArrayCollection;
import mx.controls.DataGrid;
import mx.controls.Image;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.dataGridClasses.MXDataGridItemRenderer;

import spark.components.CheckBox;

/**
* HeaderRender with a Three State Checkbox. 
* <p>Functionality:<br>
*  <li>Selecting the checkbox will select all the rows in the datagrid</li> 
*  <li>Unselecting the checkbox will unselect all the rows in the datagrid</li>
* 
* <p>The checkbox can be three states at any point:<br>
*    <li>select: This would mean that all the rows are selected</li>
*  <li>unselect: This would mean that none of the rows are selected</li>
*    <li>undecided: This would mean that one or more (but not all) of the rows are selected</li>
*  
* @author Srinivas Chekuri
* 
*/
public class ThreeStateCheckBoxHeaderRenderer extends MXDataGridItemRenderer
{
protected var myCheckBox:CheckBox;
protected var myImage:Image;
private var imageWidth:Number = 9.5;
private var imageHeight:Number = 9.5;
private var inner:String = "assets/inner.png";

private const SELECT_STATE:String="select";
private const UNSELECT_STATE:String="unselect";
private const UNDECIDED_STATE:String="undecided";

private var STATE:String = UNSELECT_STATE;

/**
* Constuctor 
* 
*/
public function ThreeStateCheckBoxHeaderRenderer()
{
super();
}

/**
* overides the function <code>createChildren</code> in the component lifecyle. This instantiates 
* <code>myCheckBox</code> and <code>myImage</code>. 
* 
*/
override protected function createChildren():void{
super.createChildren();
myCheckBox = new CheckBox();
myCheckBox.setStyle("horizontalCenter", "0");
myCheckBox.setStyle("verticalCenter", "0");
myCheckBox.addEventListener( MouseEvent.CLICK, checkBoxClickHandler );
addElement(myCheckBox);
myImage = new Image();
myImage.source = inner;
myImage.addEventListener( MouseEvent.CLICK, imageClickHandler );
myImage.visible=false;
addElement(myImage);
}

/**
* overides the function <code>updateDisplayList</code> in the component lifecyle. This sets the
* display settings of <code>myCheckBox</code> and <code>myImage</code>. This also updates the state
* according to the selections made in the datagrid.
*  
* @param unscaledWidth
* @param unscaledHeight
* 
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth,unscaledHeight);

myImage.x=myCheckBox.x+1.5;
myImage.y=myCheckBox.y+5.5;

myImage.width=imageWidth;
myImage.height=imageHeight;

if(areAllSelected()){
STATE=SELECT_STATE;
}else if(isAnyColumnSelected()){
STATE=UNDECIDED_STATE;
}else if(!isAnyColumnSelected()){
STATE=UNSELECT_STATE;
}
checkState();
}

/**
* Makes adjustments to the <code>visible</code> property of <code>myImage</code> and 
* <code>selected</code> of <code>myCheckBox</code> according to the state.
* 
* @private 
* 
*/
private function checkState():void{
if(STATE==SELECT_STATE){
myImage.visible=false;
myCheckBox.selected=true;
}else if(STATE==UNSELECT_STATE){
myImage.visible=false;
myCheckBox.selected=false;
}else{
myImage.visible=true;
myCheckBox.selected=false;
}
}

/**
* Handler method for <code>MouseEvent.CLICK</code> event on <code>myImage</code>.
*  
* @param event
* 
*/
protected function imageClickHandler(event:MouseEvent):void{
STATE=SELECT_STATE;
checkState();
selectAll();
}

/**
* Handler method for <code>MouseEvent.CLICK</code> event on <code>myCheckBox</code>.
*  
* @param event
* 
*/
protected function checkBoxClickHandler(event:MouseEvent):void{
selectAll();
}

/**
* Selects all the rows in the datagrid.  
* 
*/
private function selectAll():void{
var ac:ArrayCollection = DataGrid(DataGridListData(listData).owner).dataProvider as ArrayCollection;
for each (var o:Object in ac){
o.checked=myCheckBox.selected;
}
DataGrid(DataGridListData(listData).owner).dataProvider = ac;
}

/**
* Checks if all the rows are selected in the datagrid.
* 
* @return Boolean: returns <code>true</code> if all rows are selected else returns 
* <code>false</code>.  
* 
*/
private function areAllSelected():Boolean{
var ac:ArrayCollection = DataGrid(DataGridListData(listData).owner).dataProvider as ArrayCollection;
var b:Boolean=true;
for each (var o:Object in ac){
if(!o.checked){
b=false;
break;
}
}
return b;
}

/**
* Checks to see if any one of the rows are selected.
*  
* @return Boolean: return <code>true</code> if any one the rows are selected.
* 
*/
private function isAnyColumnSelected():Boolean{
var ac:ArrayCollection = DataGrid(DataGridListData(listData).owner).dataProvider as ArrayCollection;
var b:Boolean=false;
for each (var o:Object in ac){
if(o.checked){
b=true;
break;
}
}
return b;
}
}
}
 
  
Code for ItemRenderer:
                <?xml version="1.0" encoding="utf-8"?>
<s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
xmlns:s="library://ns.adobe.com/flex/spark" 
xmlns:mx="library://ns.adobe.com/flex/mx" 
autoDrawBackground="true">

<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.DataGrid;
import mx.controls.dataGridClasses.DataGridListData;
import mx.events.FlexEvent;

/**
* Handler function for <code>MouseEvent.CLICK</code> on checkbox.
* 
* @param event MouseEvent
* 
*/ 
public function clickHandler(event:MouseEvent):void{
var o:Object = DataGrid(DataGridListData(listData).owner).selectedItem;
o.checked=cb.selected;
ArrayCollection(DataGrid(DataGridListData(listData).owner).dataProvider).setItemAt(o,DataGrid(DataGridListData(listData).owner).selectedIndex);
}

/**
* Overides <code>data</code> parameter to set the <code>selected</code> parameter of the
* checkbox.
* 
* @param value Object
*/ 
override public function set data(value:Object):void{
if(value!=null){
super.data=value; 
cb.selected=value.checked;
}
}
]]>
</fx:Script>

<s:CheckBox id="cb" click="clickHandler(event)" horizontalCenter="0" verticalCenter="0"/>
</s:MXDataGridItemRenderer>
   
    
I have also attached the code for your reference.

+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes