달력

12

« 2024/12 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

'ItemRenderer'에 해당되는 글 1

  1. 2008.12.13 A 3-state checkbox in a TreeItemRenderer
2008. 12. 13. 11:42

A 3-state checkbox in a TreeItemRenderer Enjoy/FLEX2008. 12. 13. 11:42


출처 : https://store1.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&postid=545&loc=en_US&productid=2


A 3-state checkbox in a TreeItemRenderer

by Cryptodude on October 18, 2006 Avg Rating 3.8 (32)   |   Log in to rate post.

Tagged with Components , ActionScript

Problem Summary

Trees are commonly used to represent file systems. Often the user needs to select several items within several folders and take an action (copy, delete, print, ...) on them, so, there needs to be some visual mechanism for indicating that a node is selected. A checkbox is typically used to represent selection. What we need is a tree of 3-state checkboxes.

Solution Summary

There are three main aspects to the solution: 1. A TreeItemRenderer is created to place a CheckBox control at each node in the tree. 2. An image of a tiny black box is painted on top of the CheckBox when the CheckBox is in the third state. 3. The underlying data model for the tree needs to contain an attribute representing the state of the CheckBox.

Explanation

Selection/de-selection of a parent node should cause children nodes to be selected/de-selected. In the case that a parent node has some children that are selected and some that are not selected, a simple 2-state check box won’t do - this third state (where some of the children of a node are selected and some are not) cannot be represented by the selected property of a check box, which is a Boolean.

The solution lies in the implementation of the TreeItemRenderer class. I have created an ActionScript class called CheckTreeRenderer for this purpose.

The first thing to do is override the createChildren method. This method is responsible for creating each node in the tree. Here we create a CheckBox and an Image.

override protected function createChildren():void
{
   super.createChildren();
   myCheckBox = new CheckBox();
   myCheckBox.setStyle( "verticalAlign", "middle" );
   myCheckBox.addEventListener( MouseEvent.CLICK, checkBoxToggleHandler );
   addChild(myCheckBox);
   myImage = new Image();
   myImage.source = inner;
   myImage.addEventListener( MouseEvent.CLICK, imageToggleHandler );
   myImage.setStyle( "verticalAlign", "middle" );
   addChild(myImage);
}   

Each child control, CheckBox and Image, needs to handle mouse clicks, so we create an EventListener for each:

private function checkBoxToggleHandler(event:MouseEvent):void
{
   if (data)
   {
      var myListData:TreeListData = TreeListData(this.listData);
      var selectedNode:Object = myListData.item;
      var tree:Tree = Tree(myListData.owner);
      var toggle:Boolean = myCheckBox.selected;
      if (toggle)
      {
         toggleChildren(data, tree, STATE_CHECKED);
      }
      else
      {
         toggleChildren(data, tree, STATE_UNCHECKED);
      }
      var parent:Object = tree.getParentItem (data);
      toggleParents (parent, tree, getState (tree, parent));
    }
}

private function imageToggleHandler(event:MouseEvent):void
{
   myCheckBox.selected = !myCheckBox.selected;
   checkBoxToggleHandler(event);
}

The handler for the Image control delegates handling most of the selection logic to the CheckBox handler. After all, the Image control is only for handling the third state.

For each node clicked, the CheckBox handler toggles the state of the node’s children and then it toggles the state of the node’s parent(s). The children can only be set to a CHECKED or UNCHECKED state whereas the parent(s) can be also set to the third state. This third state, called the SCHRODINGER state, occurs when some of the parent node’s children are CHECKED state, UNCHECKED state and/or in SCHRODINGER state.

The state that the parent will be set to is arrived at by looking at the state of its children. This is the job of the getState method:

private function getState(tree:Tree, parent:Object):String
{
   var noChecks:int = 0;
   var noCats:int = 0;
   var noUnChecks:int = 0;
   if (parent != null)
   {
      var treeData:ITreeDataDescriptor = tree.dataDescriptor;
      var cursor:IViewCursor = treeData.getChildren(parent).createCursor();
      while (!cursor.afterLast)
      {
         if (cursor.current.@state == STATE_CHECKED)
         {
            noChecks++;
         }
         else if (cursor.current.@state == STATE_UNCHECKED)
         {
            noUnChecks++
         }
         else
         {
            noCats++;
         }
         cursor.moveNext();
      }
   }
   if ((noChecks > 0 && noUnChecks > 0) || (noCats > 0))
   {
         return STATE_SCHRODINGER;
   }
   else if (noChecks > 0)
   {
         return STATE_CHECKED;
   }
   else
   {
         return STATE_UNCHECKED;
   }
}

The toggler code follows:

private function toggleParents (item:Object, tree:Tree, state:String):void
{
   if (item == null)
   {
      return;
   }
   else
   {
      item.@state = state;
      toggleParents(tree.getParentItem(item), tree, getState (tree,
      tree.getParentItem(item)));
   }
}

private function toggleChildren (item:Object, tree:Tree, state:String):void
{
   if (item == null)
   {
      return;
   }
   else
   {
      item.@state = state;
      var treeData:ITreeDataDescriptor = tree.dataDescriptor;
      if (treeData.hasChildren(item))
      {
         var children:ICollectionView = treeData.getChildren (item);
         var cursor:IViewCursor = children.createCursor();
         while (!cursor.afterLast)
         {
            toggleChildren(cursor.current, tree, state);
            cursor.moveNext();
         }
      }
   }
}

To actually set the state of a node, we override the property setter for data:

private function setCheckState (checkBox:CheckBox, value:Object, state:String):void
{
   if (state == STATE_CHECKED)
   {
      checkBox.selected = true;
   }
   else if (state == STATE_UNCHECKED)
   {
      checkBox.selected = false;
   }
   else if (state == STATE_SCHRODINGER)
   {
      checkBox.selected = false;
   }
}       

override public function set data(value:Object):void
{
   super.data = value;
   
   var _tree:Tree = Tree(this.parent.parent);
   setCheckState (myCheckBox, value, value.@state);
   if(TreeListData(super.listData).item.@isBranch == 'true')
   {
      _tree.setStyle("defaultLeafIcon", null);
   }
}

Finally, we paint the CheckBox and Image on the screen:

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
   super.updateDisplayList(unscaledWidth, unscaledHeight);
   if(super.data)
   {
      if (super.icon != null)
      {
         myCheckBox.x = super.icon.x;
         myCheckBox.y = 2;
         super.icon.x = myCheckBox.x + myCheckBox.width + 17;
         super.label.x = super.icon.x + super.icon.width + 3;
      }
      else
      {
         myCheckBox.x = super.label.x;
         myCheckBox.y = 2;
         super.label.x = myCheckBox.x + myCheckBox.width + 17;
      }
      if (data.@state == STATE_SCHRODINGER)
      {
         myImage.x = myCheckBox.x + 4;
         myImage.y = myCheckBox.y + 4;
         myImage.width = imageWidth;
         myImage.height = imageHeight;
      }
      else
      {
         myImage.x = 0;
         myImage.y = 0;
         myImage.width = 0;
         myImage.height = 0;
      }
   }
} 

I’ve included a sample application which makes use of the CheckTreeRenderer. Also, a PNG file is included for the Image. Hopefully, someone will make this code better. If so, please send me a copy. Thanks.

CheckTree.zip




Tagged with Components , ActionScript

Tag it on del.icio.us or Digg

:
Posted by 라면스프