Flex Checkbox TreeItemRenderer

 

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>

8 Comments - Average Rating:4

Comments:
i want the parent node also a checkbox and if we select the parent one all the child nodes should also select and if i deselect one of the child node then it should deselect only the appropriate one not all
Rating: 4
Date Posted: July 26th, 2012


Great post! Helped me out a lot.
Rating: 5
Date Posted: April 29th, 2012


Well if it helps anyone , I think I have a fix for the keyboard nav issue.

What I did was created a custom Tree control and in it's constructor added:

super.addEventListener(ListEvent.CHANGE, treeListChangeHandler, false, 0, true);

Then in the handler:
private function treeListChangeHandler(event:ListEvent):void
{
(event.itemRenderer as ReportListTreeItemRenderer).setFocus();
}

I then added the following into my custom item renderer

override public function setFocus():void
{
super.setFocus();
checkBox.setFocus();
}

Hope that helps some folks out.

For the tree's "change" event issue I simply switched to the "itemClick" ListEvent instead as all the internal processing was done by that point and the XML dataProvider was updated.

Hopefully that helps some out!
Rating: 4
Date Posted: December 2nd, 2009


Thanks for this info. Couple of issues though. Using the arrow keys to navigate the tree and the spacebar to select items seems to keep focus at the last item you mouse clicked on. the only way I see to fix this is to write a custom tree control and handle the selectedIndex manually based on the checkbox selected (the XML data actually)

The other issues I'm trying to solve is how to have the tree dispatch the "change" event only after the checkbox click or change has fired. I want to handle the "change" event from the tree to update a master list of the items selected as they go.

Anyway, your post gave me a great start and it's appreciated.
Rating: 4
Date Posted: December 2nd, 2009


Thanks,but I have a problem: this demo can't respond the SpaceBar key.If you use the Down and the Spacebar to selected checkbox.
Rating: 3
Date Posted: December 2nd, 2009


Thanks for the post. These addChild techniques do not seem to work when the child component is larger than the typical node label.
Many other exmaples on the web try this and they fail on scrolling.
www.fonepays.com/flex/
Rating: 4
Date Posted: November 13th, 2009


I've added the code to parse the data provider. Hope this helps.
Rating: 4
Date Posted: September 6th, 2009


How do you parse the tree to get the Save button to display which items are checked?
Rating: 4
Date Posted: September 6th, 2009



RECENT ARTICLES