Flex Checkbox TreeItemRenderer
October 20th, 2009
Here's a TreeItemRenderer that creates checkboxes which are used to allow users to set permissions for an application. ItemRenderers can be tricky because the virtual machine re-uses them as the display list is updated. So you can get strange results if you're not careful. When a CheckBox is clicked the TreeItemRenderer updates the value a @checked attribute in the corresponding xml node of the dataProvider.
You might have to be careful when setting some properties of the Tree. For example, you'll want to set the 'folderClosedIcon', 'folderOpenIcon', and 'defaultLeafIcon' properties to null. You could do this in the TreeItemRenderer code, but I figured it would be more efficient to set the properties of the Tree directly.
Here's the code for the TreeItemRenderer:
package
{
import mx.controls.treeClasses.TreeItemRenderer;
import flash.events.Event;
import flash.events.MouseEvent;
import mx.controls.CheckBox;
import mx.controls.treeClasses.*;
public class PermissionsTreeItemRendererV2 extends TreeItemRenderer{
public var chk:CheckBox;
public var itemXml:XML;
public function PermissionsTreeItemRendererV2(){
super();
mouseEnabled = false;
}
override public function set data(value:Object):void{
if(value != null){
super.data = value;
this.itemXml = XML(value);
if(this.itemXml.@checked == "1"){
this.chk.selected = true;
}else{
this.chk.selected = false;
}
}
}
override protected function createChildren():void{
super.createChildren();
chk = new CheckBox();
chk.addEventListener(MouseEvent.CLICK, handleChkClick);
addChild(chk);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth,unscaledHeight);
if(super.data){
var tld:TreeListData = TreeListData(super.listData);
//In some cases you only want a checkbox to appear if an
//item is a leaf
//if so, then keep the following block uncommented,
//otherwise you can comment it out to display the checkbox
//for branch nodes
if(tld.hasChildren){
this.chk.visible = false;
}else{
//You HAVE to have the else case to set visible to true
//even though you'd think the default would be visible
//it's an issue with itemrenderers...
this.chk.visible = true;
}
if(chk.visible){
//if the checkbox is visible then
//reposition the controls to make room for checkbox
this.chk.x = super.label.x
super.label.x = this.chk.x + 17;
this.chk.y = super.label.y+8;
}
}
}
private function handleChkClick(evt:MouseEvent):void{
if(this.chk.selected){
//this.checked = true;
this.itemXml.@checked = "1";
}else{
//this.checked = false;
this.itemXml.@checked = "0";
}
}
}
}
Here's the mxml component that makes use of the TreeItemRenderer...
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
[Bindable]
private var permissionsXML:XML =
<permissions>
<permission pId="1" pName="Security Permissions" pParentID="0" checked="0">
<permission pId="2" pName="User Create" pParentID="1" checked="0"/>
<permission pId="3" pName="User Edit" pParentID="1" checked="0"/>
<permission pId="4" pName="User Group Create" pParentID="1" checked="0"/>
<permission pId="5" pName="User Group Edit" pParentID="1" checked="0">
<permission pId="48" pName="Edit Group Members" pParentID="5" checked="0"/>
<permission pId="49" pName="Edit Group Permissions" pParentID="5" checked="0"/>
</permission>
</permission>
<permission pId="11" pName="Content Permissions" pParentID="0" checked="0">
<permission pId="12" pName="Content Create" pParentID="11" checked="0"/>
<permission pId="13" pName="Content View" pParentID="11" checked="0"/>
<permission pId="14" pName="Content Edit" pParentID="11" checked="0">
<permission pId="54" pName="Edit Content Date" pParentID="14" checked="0"/>
<permission pId="55" pName="Edit Content Creator" pParentID="14" checked="0"/>
<permission pId="56" pName="Edit Content Status" pParentID="14" checked="0"/>
<permission pId="58" pName="Edit Content Image" pParentID="14" checked="0"/>
</permission>
</permission>
</permissions>;
private function save():void{
//create an XMLList of all 'Permission' nodes whose 'checked'
//attribute is set to 1...
var xList:XMLList = permissionsXML.descendants().(@checked == "1");
var str:String = new String();
str = "The following permissions are checked.\n";
//loop through xList and add the 'pDisplayName' attribute
//to the string...
for (var x:uint=0; x < xList.length(); x++){
str+=xList[x].@pName + "\n";
}
Alert.show(str);
}
]]>
</mx:Script>
<mx:Button label="SAVE" click="save()"/>
<mx:Tree id="trPermissions" dataProvider="{permissionsXML}"
itemRenderer="PermissionsTreeItemRendererV2"
showRoot="false"
paddingTop="5"
labelField="@pName"
height="100%"
width="300"
backgroundAlpha="0"
folderClosedIcon="{null}"
folderOpenIcon="{null}"
defaultLeafIcon="{null}"
mouseEnabled="false"
borderStyle="none"
verticalScrollPolicy="auto"/>
</mx:VBox>