Thursday, August 8, 2013

Flex: Creating a Checkbox with 3 states...

There will be situations that requires a list of checkboxes and you are giving the user the option of selecting and unselecting the individual options. However, the normal checkbox might work for the individual selections but it will not work nicely for the header. Therefore...

Main Application Source Code - Simple3StateCheckBox.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:Declarations>
  <!-- Place non-visual elements (e.g., services, value objects) here -->
 </fx:Declarations>
 <fx:Style source="css/style.css"/>
 <fx:Script>
  <![CDATA[
   import events.CustomCheckBoxEvents;
   
   import mx.collections.ArrayCollection;
   import mx.events.FlexEvent;
   
   import object.SingletonObject;
   
   /**
    * We are using a singleton object to store the objects/data.
    */
   private var dataObj:SingletonObject = 
    SingletonObject.getInstance();
   /**
    * Upon creationComplete, we need to add all the Listeners
    * and check the appropriate state that we should be
    * displaying for the header.
    */
   protected function creationCompleteHandler(event:FlexEvent):void
   {
    this.addEventListener(
     CustomCheckBoxEvents.SELECT_ALL_ITEMS,
     checkBoxEvent);
    this.addEventListener(
     CustomCheckBoxEvents.UNSELECT_ALL_ITEMS,
     checkBoxEvent);
    this.addEventListener(
     CustomCheckBoxEvents.SELECT_AN_ITEM,
     checkBoxEvent);
    this.addEventListener(
     CustomCheckBoxEvents.UNSELECT_AN_ITEM,
     checkBoxEvent);
    updateHeaderState();
   }
   
   /**
    * All the events are handled over here.
    */
   private function checkBoxEvent(event:CustomCheckBoxEvents):void
   {
    for each(var item:Object in dataObj.expenses)
    {
     if(event.type == CustomCheckBoxEvents.SELECT_ALL_ITEMS)
     {
      item.selected = true;
     }else if(event.type == CustomCheckBoxEvents.UNSELECT_ALL_ITEMS)
     {
      item.selected = false;
     }else if(item.id == event.data.id)
     {
      if(event.type == 
       CustomCheckBoxEvents.SELECT_AN_ITEM)
      {
       item.selected = true;
      }else if(event.type == 
       CustomCheckBoxEvents.UNSELECT_AN_ITEM)
      {
       item.selected = false;
      }
     }
    }
    dataObj.expenses.refresh();
    updateHeaderState();
   }
   
   /**
    * Function to determine that best state that we are
    * showing for the header.
    */
   private function updateHeaderState():void
   {
    var count:Number = 0;
    var maxCount:Number = 0;
    for each(var item:Object in dataObj.expenses)
    {
     maxCount ++;
     if(item.selected)
     {
      count ++;
     }
    }
    if(count == 0)
    {
     dataObj.selectedState = 0;
    }else if(count == maxCount)
    {
     dataObj.selectedState = 2;
    }else{
     dataObj.selectedState = 1;
    }
   }
  ]]>
 </fx:Script>
 <s:VGroup width="100%"
     height="100%" 
     verticalAlign="middle" 
     horizontalAlign="center">
  <s:DataGrid width="50%"
      height="50%" 
     dataProvider="{dataObj.expenses}">
   <s:columns>
    <s:ArrayList>
     <s:GridColumn dataField="selected" 
          width="20"
          headerRenderer="renderers.CustomHeaderCheckBoxRenderer"
          itemRenderer="renderers.CustomCheckBoxRenderer"/>
     <s:GridColumn dataField="amount"/>
    </s:ArrayList>
   </s:columns>
  </s:DataGrid>
 </s:VGroup>
</s:Application>

Event class for the checkbox - events/CustomCheckBoxEvents.as
package events
{
 import flash.events.Event;
 
 public class CustomCheckBoxEvents extends Event
 {
  //All the different event type
  public static const SELECT_ALL_ITEMS:String = 
   "selectAllItems";
  public static const UNSELECT_ALL_ITEMS:String = 
   "unselectAllItems";
  public static const SELECT_AN_ITEM:String = 
   "selectAnItem";
  public static const UNSELECT_AN_ITEM:String = 
   "unselectAnItem";
  
  private var _data:Object = null;

  public function get data():Object
  {
   return _data;
  }

  public function set data(value:Object):void
  {
   _data = value;
  }
  
  public function CustomCheckBoxEvents(type:String, tempData:Object = null, bubbles:Boolean=true, cancelable:Boolean=false)
  {
   super(type, bubbles, cancelable);
   _data = tempData;
  }
  
  override public function clone():Event
  {
   // TODO Auto Generated method stub
   return new CustomCheckBoxEvents(type, data, bubbles, cancelable);
  }
 }
}

Singleton Object class to store the neccessary data - object/SingletonObject.as
package object
{
 import mx.collections.ArrayCollection;

 public class SingletonObject
 {
  private static var _instance:SingletonObject=null;
  
  /**
   * You need the following functions to create a
   * singleton Object. SingletonObject(e:SingletonEnforcer)
   * and getInstance():SingletonObject
   * Rather than using new SingletonObject to create a new
   * object of the class, you need to use
   * SingletonObject.getInstance() to point to the Singleton
   * class.
   */
  public function SingletonObject(e:SingletonEnforcer){
   trace("new singleton object created");
  }

  public static function getInstance():SingletonObject{
   if(_instance==null){
    _instance=new SingletonObject(new SingletonEnforcer);
   }
   return _instance;
  }
  
  /**
   * Here's our data for the DataGrid.
   */
  private var _expenses:ArrayCollection = new ArrayCollection([
   {selected:true, id:0, amount:2000},
   {selected:true, id:1, amount:1000},
   {selected:true, id:2, amount:100},
   {selected:true, id:3, amount:450},
   {selected:true, id:4, amount:100},
   {selected:true, id:5, amount:200}
  ]);
  
  [Bindable]
  public function get expenses():ArrayCollection
  {
   return _expenses;
  }
  
  public function set expenses(value:ArrayCollection):void
  {
   _expenses = value;
  }
  
  private var _selectedState:Number = 0;
  
  [Bindable]
  public function get selectedState():Number
  {
   return _selectedState;
  }

  public function set selectedState(value:Number):void
  {
   _selectedState = value;
  }
 }
}

/**
 * This class is needed in creating a singleton class.
 */
class SingletonEnforcer{
 
}

Renderers class for the header that comes with a custom checkbox with 3 different states - renderers/CustomHeaderCheckBoxRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:DefaultGridHeaderRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" 
        xmlns:mx="library://ns.adobe.com/flex/mx" 
        xmlns:component="renderers.component.*" 
        width="100%" click="clickHandler(event)">
 <fx:Declarations>
  <!-- Place non-visual elements (e.g., services, value objects) here -->
 </fx:Declarations>
 <fx:Script>
  <![CDATA[
   import object.SingletonObject;
   /**
    * We are using a singleton object to store the objects/data.
    */
   private var dataObj:SingletonObject = 
    SingletonObject.getInstance();
   
   protected function clickHandler(event:MouseEvent):void
   {
    event.stopImmediatePropagation();
   }
   
  ]]>
 </fx:Script>
 <s:states>
  <s:State name="normal" />
  <s:State name="hovered" />
  <s:State name="down" />
 </s:states>     
 
 <!-- layer 1: shadow -->
 <!--- @private -->
 <s:Rect id="shadow" left="-1" right="-1" top="-1" bottom="-1" radiusX="2">
  <s:fill>
   <s:LinearGradient rotation="90">
    <s:GradientEntry color="0x000000" 
         color.down="0xFFFFFF"
         alpha="0.01"
         alpha.down="0" />
    <s:GradientEntry color="0x000000" 
         color.down="0xFFFFFF" 
         alpha="0.07"
         alpha.down="0.5" />
   </s:LinearGradient>
  </s:fill>
 </s:Rect>
 
 <!-- layer 2: fill -->
 <!--- @private -->
 <s:Rect id="fill" left="0" right="0" top="0" bottom="0">
  <s:fill>
   <s:LinearGradient rotation="90">
    <s:GradientEntry color="0xFFFFFF" 
         color.hovered="0xBBBDBD" 
         color.down="0xAAAAAA" 
         alpha="0.85" />
    <s:GradientEntry color="0xD8D8D8" 
         color.hovered="0x9FA0A1" 
         color.down="0x929496" 
         alpha="0.85" />
   </s:LinearGradient>
  </s:fill>
 </s:Rect>
 
 <!-- layer 3: fill lowlight -->
 <!--- @private -->
 <s:Rect id="lowlight" left="0" right="0" top="0" bottom="0">
  <s:fill>
   <s:LinearGradient rotation="270">
    <s:GradientEntry color="0x000000" ratio="0.0" alpha="0.0627" />
    <s:GradientEntry color="0x000000" ratio="0.48" alpha="0.0099" />
    <s:GradientEntry color="0x000000" ratio="0.48001" alpha="0" />
   </s:LinearGradient>
  </s:fill>
 </s:Rect>
 
 <!-- layer 4: fill highlight -->
 <!--- @private -->
 <s:Rect id="highlight" left="0" right="0" top="0" bottom="0">
  <s:fill>
   <s:LinearGradient rotation="90">
    <s:GradientEntry color="0xFFFFFF"
         ratio="0.0"
         alpha="0.33" 
         alpha.hovered="0.22" 
         alpha.down="0.12"/>
    <s:GradientEntry color="0xFFFFFF"
         ratio="0.48"
         alpha="0.33"
         alpha.hovered="0.22"
         alpha.down="0.12" />
    <s:GradientEntry color="0xFFFFFF"
         ratio="0.48001"
         alpha="0" />
   </s:LinearGradient>
  </s:fill>
 </s:Rect>  
 
 <!-- layer 5: highlight stroke (all states except down) -->
 <!--- @private -->
 <s:Rect id="highlightStroke" left="0" right="0" top="0" bottom="0" excludeFrom="down">
  <s:stroke>
   <s:LinearGradientStroke rotation="90" weight="1">
    <s:GradientEntry color="0xFFFFFF" alpha.hovered="0.22" />
    <s:GradientEntry color="0xD8D8D8" alpha.hovered="0.22" />
   </s:LinearGradientStroke>
  </s:stroke>
 </s:Rect>
 
 <!-- layer 6: highlight stroke (down state only) -->
 <!--- @private -->
 <s:Rect id="hldownstroke1" left="0" right="0" top="0" bottom="0" includeIn="down">
  <s:stroke>
   <s:LinearGradientStroke rotation="90" weight="1">
    <s:GradientEntry color="0x000000" alpha="0.25" ratio="0.0" />
    <s:GradientEntry color="0x000000" alpha="0.25" ratio="0.001" />
    <s:GradientEntry color="0x000000" alpha="0.07" ratio="0.0011" />
    <s:GradientEntry color="0x000000" alpha="0.07" ratio="0.965" />
    <s:GradientEntry color="0x000000" alpha="0.00" ratio="0.9651" />
   </s:LinearGradientStroke>
  </s:stroke>
 </s:Rect>
 <!--- @private -->
 <s:Rect id="hldownstroke2" left="1" right="1" top="1" bottom="1" includeIn="down">
  <s:stroke>
   <s:LinearGradientStroke rotation="90" weight="1">
    <s:GradientEntry color="0x000000" alpha="0.09" ratio="0.0" />
    <s:GradientEntry color="0x000000" alpha="0.00" ratio="0.0001" />
   </s:LinearGradientStroke>
  </s:stroke>
 </s:Rect>
 <s:VGroup verticalAlign="middle" 
     horizontalAlign="center" 
     width="100%"
     height="100%">
  <component:CustomCheckBox styleName="customCheckBox"
          selectedValue="{dataObj.selectedState}"/>
 </s:VGroup>
</s:DefaultGridHeaderRenderer>


Custom checkbox that supports 3 different states - renderers/component/CustomCheckBox.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:CheckBox xmlns:fx="http://ns.adobe.com/mxml/2009" 
   xmlns:s="library://ns.adobe.com/flex/spark" 
   xmlns:mx="library://ns.adobe.com/flex/mx" 
   click="clickHandler(event)" 
   mouseOver="mouseOverHandler(event)" 
   mouseDown="mouseDownHandler(event)" 
   mouseOut="mouseOutHandler(event)">
 <!-- Declaring all the different types of state in this component -->
 <fx:Metadata>
  [SkinState("up")]
  [SkinState("over")]
  [SkinState("down")]
  [SkinState("disabled")]
  [SkinState("upAndSelected")]
  [SkinState("overAndSelected")]
  [SkinState("downAndSelected")]
  [SkinState("disabledAndSelected")]
  [SkinState("upAndSemiSelected")]
  [SkinState("overAndSemiSelected")]
  [SkinState("downAndSemiSelected")]
  [SkinState("disabledAndSemiSelected")]
 </fx:Metadata>
 <fx:Script>
  <![CDATA[
   import events.CustomCheckBoxEvents;
   /**
    * Rather than select and unselected state,
    * we need 3 different states now.
    * 
    * 0 - not selected
    * 1 - semi selected
    * 2 - selected
    * 
    * @default 0
    */
   private var _selectedValue:Number = 0;

   public function get selectedValue():Number
   {
    return _selectedValue;
   }

   public function set selectedValue(value:Number):void
   {
    _selectedValue = value;
    updateState();
   }
   
   /**
    * For storing the component state
    * 
    * 0 - up
    * 1 - over
    * 2 - down
    * 
    * @default 0
    */
   private var currentComponentState:Number = 0;
   
   /**
    * For the skin file to find out the state that it
    * should be displaying.
    */
   override protected function getCurrentSkinState():String
   {
    return this.currentState;
   }
   
   /**
    * Upon clicking on this component, we need to raise
    * a few events.
    */
   override protected function clickHandler(event:MouseEvent):void
   {
    event.stopImmediatePropagation();
    switch (_selectedValue)
    {
     //when unselected
     case 0:
      _selectedValue = 2;
      this.dispatchEvent(
       new CustomCheckBoxEvents(
        CustomCheckBoxEvents.SELECT_ALL_ITEMS));
      break;
     //when semi selected
     case 1:
      _selectedValue = 2;
      this.dispatchEvent(
       new CustomCheckBoxEvents(
        CustomCheckBoxEvents.SELECT_ALL_ITEMS));
      break;
     //when selected
     case 2:
      _selectedValue = 0;
      this.dispatchEvent(
       new CustomCheckBoxEvents(
        CustomCheckBoxEvents.UNSELECT_ALL_ITEMS));
      break;
    }
    updateState();
   }
   
   /**
    * Upon mouse over on this component, we need to update
    * the state.
    */
   protected function mouseOverHandler(event:MouseEvent):void
   {
    event.stopImmediatePropagation();
    currentComponentState = 1;
    updateState();
   }
   
   /**
    * Upon mouse down on this component, we need to update
    * the state.
    */
   protected function mouseDownHandler(event:MouseEvent):void
   {
    event.stopImmediatePropagation();
    currentComponentState = 2;
    updateState();
   }
   
   /**
    * Upon mouse out on this component, we need to update
    * the state.
    */
   protected function mouseOutHandler(event:MouseEvent):void
   {
    event.stopImmediatePropagation();
    currentComponentState = 0;
    updateState();
   }
   
   private function updateState():void
   {
    var tempStr:String = "";
    switch(_selectedValue)
    {
     //when unselected
     case 0:
      tempStr = "";
      break;
     //when semi selected
     case 1:
      tempStr = "AndSemiSelected";
      break;
     //when selected
     case 2:
      tempStr = "AndSelected";
      break;
    }
    if(this.enabled)
    {
     switch(currentComponentState)
     {
      //when mouse out
      case 0:
       tempStr = "up" + tempStr;
       break;
      //when mouse over
      case 1:
       tempStr = "over" + tempStr;
       break;
      //when mouse down
      case 2:
       tempStr = "down" + tempStr;
       break;
     }
    }else{
     tempStr = "disabled" + tempStr;
    }
    this.currentState = tempStr;
   }
  ]]>
 </fx:Script>
 <!-- 
  Upon swaping to these states, we need to call the function
  invalidateSkinState() to update the skin immediately.
 -->
 <s:states>
  <s:State name="up" enterState="invalidateSkinState()"/>
  <s:State name="over" enterState="invalidateSkinState()"/>
  <s:State name="down" enterState="invalidateSkinState()"/>
  <s:State name="disabled" enterState="invalidateSkinState()"/>
  <s:State name="upAndSelected" enterState="invalidateSkinState()"/>
  <s:State name="overAndSelected" enterState="invalidateSkinState()"/>
  <s:State name="downAndSelected" enterState="invalidateSkinState()"/>
  <s:State name="disabledAndSelected" enterState="invalidateSkinState()"/>
  <s:State name="upAndSemiSelected" enterState="invalidateSkinState()"/>
  <s:State name="overAndSemiSelected" enterState="invalidateSkinState()"/>
  <s:State name="downAndSemiSelected" enterState="invalidateSkinState()"/>
  <s:State name="disabledAndSemiSelected" enterState="invalidateSkinState()"/>
 </s:states>
</s:CheckBox>

Skin class for the custom checkbox - skins/CustomCheckBoxSkin.mxml
<?xml version="1.0" encoding="utf-8"?>

<!--

    ADOBE SYSTEMS INCORPORATED
    Copyright 2008 Adobe Systems Incorporated
    All Rights Reserved.

    NOTICE: Adobe permits you to use, modify, and distribute this file
    in accordance with the terms of the license agreement accompanying it.

-->

<!--- The default skin class for the Spark CheckBox component.  

      @see spark.components.CheckBox
        
      @langversion 3.0
      @playerversion Flash 10
      @playerversion AIR 1.5
      @productversion Flex 4
-->
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
    alpha.disabledStates="0.5">
 <fx:Metadata>[HostComponent("renderers.component.CustomCheckBox")]</fx:Metadata>

    <fx:Script fb:purpose="styling">
        /* Define the skin elements that should not be colorized. 
           For button, the graphics are colorized but the label is not. */
        static private const exclusions:Array = ["labelDisplay", "check"];

       /** 
        * @private 
        */     
        override public function get colorizeExclusions():Array {return exclusions;}
        
        /* Define the symbol fill items that should be colored by the "symbolColor" style. */
        static private const symbols:Array = ["checkMarkFill", "semiCheckMarkFill"];

       /**
        * @private 
        */
        override public function get symbolItems():Array {return symbols};
        
        /**
         * @private
         */
        override protected function initializationComplete():void
        {
            useChromeColor = true;
            super.initializationComplete();
        }
    </fx:Script>
    
    <fx:Script>
        <![CDATA[
            /** 
             * @private 
             */     
            private static const focusExclusions:Array = ["labelDisplay"];

            /**
             * @private
             */
            override public function get focusSkinExclusions():Array { return focusExclusions;};
        ]]>
    </fx:Script>
    
 <!--
  All the states of this component.
 -->
    <s:states>
        <s:State name="up" />
        <s:State name="over" stateGroups="overStates" />
        <s:State name="down" stateGroups="downStates" />
        <s:State name="disabled" stateGroups="disabledStates" />
        <s:State name="upAndSelected" stateGroups="selectedStates" />
        <s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
        <s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
        <s:State name="disabledAndSelected" stateGroups="disabledStates, selectedStates" />
  <s:State name="upAndSemiSelected" stateGroups="semiSelectedStates" />
  <s:State name="overAndSemiSelected" stateGroups="overStates, semiSelectedStates" />
  <s:State name="downAndSemiSelected" stateGroups="downStates, semiSelectedStates" />
  <s:State name="disabledAndSemiSelected" stateGroups="disabledStates, semiSelectedStates" />  
    </s:states>
    
    <s:Group verticalCenter="0" width="13" height="13" layoutDirection="ltr">
        <!-- drop shadow -->
        <s:Rect left="-1" top="-1" right="-1" bottom="-1">
            <s:stroke>
                <s:LinearGradientStroke rotation="90" weight="1">
                    <s:GradientEntry color="0x000000" 
                                   color.downStates="0xFFFFFF"
                                   alpha="0.011"
                                   alpha.downStates="0" />
                    <s:GradientEntry color="0x000000" 
                                   color.downStates="0xFFFFFF" 
                                   alpha="0.121"
                                   alpha.downStates="0.57" />
                </s:LinearGradientStroke>
            </s:stroke>
        </s:Rect>
        
        <!-- fill -->
        <s:Rect left="1" top="1" right="1" bottom="1">
            <s:fill>
                <s:LinearGradient rotation="90">
                    <s:GradientEntry color="0xFFFFFF" 
                                   color.overStates="0xBBBDBD" 
                                   color.downStates="0xAAAAAA" 
                                   alpha="0.85" />
                    <s:GradientEntry color="0xD8D8D8" 
                                   color.overStates="0x9FA0A1" 
                                   color.downStates="0x929496" 
                                   alpha="0.85" />
                </s:LinearGradient>
            </s:fill>
        </s:Rect>
        
        <!-- fill highlight -->
        <s:Rect left="1" right="1" top="1" height="5">
            <s:fill>
                <s:SolidColor color="0xFFFFFF" alpha="0.33" alpha.downStates="0" />
            </s:fill>
        </s:Rect>
        
        <!-- layer 6: highlight stroke (all states except down) -->
        <s:Rect left="1" right="1" top="1" bottom="1" excludeFrom="downStates">
            <s:stroke>
                <s:LinearGradientStroke rotation="90" weight="1">
                    <s:GradientEntry color="0xFFFFFF" alpha.overStates="0.33" />
                    <s:GradientEntry color="0xFFFFFF" alpha="0.12" alpha.overStates="0.0396" />
                </s:LinearGradientStroke>
            </s:stroke>
        </s:Rect>
        
        <!-- layer 6: highlight stroke (down state only) -->
        <s:Rect left="1" top="1" bottom="1" width="1" includeIn="downStates">
            <s:fill>
                <s:SolidColor color="0x000000" alpha="0.07" />
            </s:fill>
        </s:Rect>
        <s:Rect right="1" top="1" bottom="1" width="1" includeIn="downStates">
            <s:fill>
                <s:SolidColor color="0x000000" alpha="0.07" />
            </s:fill>
        </s:Rect>
        <s:Rect left="1" top="1" right="1" height="1" includeIn="downStates">
            <s:fill>
                <s:SolidColor color="0x000000" alpha="0.25" />
            </s:fill>
        </s:Rect>
        <s:Rect left="1" top="2" right="1" height="1" includeIn="downStates">
            <s:fill>
                <s:SolidColor color="0x000000" alpha="0.09" />
            </s:fill>
        </s:Rect>
        
        <!-- border - put on top of the fill so it doesn't disappear when scale is less than 1 -->
        <s:Rect left="0" top="0" right="0" bottom="0">
            <s:stroke>
                <s:LinearGradientStroke rotation="90" weight="1">
                    <s:GradientEntry color="0x000000" 
                                   alpha="0.5625"
                                   alpha.downStates="0.6375" />
                    <s:GradientEntry color="0x000000" 
                                   alpha="0.75"
                                   alpha.downStates="0.85" />
                </s:LinearGradientStroke>
            </s:stroke>
        </s:Rect>
    
        <!-- checkmark -->
        <!--- The checkmark in the box for this skin. To create a custom check mark, create a custom skin class. -->
        <s:Path left="2" top="0" includeIn="selectedStates" id="check" itemCreationPolicy="immediate"
                data="M 9.2 0.1 L 4.05 6.55 L 3.15 5.0 L 0.05 5.0 L 4.6 9.7 L 12.05 0.1 L 9.2 0.1">        
            <s:fill>
                <!--- 
                    @private 
                    The solid color fill for the CheckBox's checkmark. The default alpha is .9, and the default fill color is 0x000000. 
                -->
                <s:SolidColor id="checkMarkFill" color="0" alpha="0.8" />
            </s:fill>
        </s:Path>
  
  <!-- semi selected checkmark -->
  <!--- The checkmark in the box for this skin. To create a custom check mark, create a custom skin class. -->
  <s:Path left="2" top="2" includeIn="semiSelectedStates" id="semiCheck" itemCreationPolicy="immediate"
    data="M 1 1 L 10 1 L 10 10 L 1 10 L 1 1">        
   <s:fill>
    <!--- 
    @private 
    The solid color fill for the CheckBox's checkmark. The default alpha is .9, and the default fill color is 0x000000. 
    -->
    <s:SolidColor id="semiCheckMarkFill" color="0" alpha="0.8" />
   </s:fill>
  </s:Path>
    </s:Group>

    <!-- Label -->
    <!--- @copy spark.components.supportClasses.ButtonBase#labelDisplay -->
    <s:Label id="labelDisplay"
             textAlign="start"
             verticalAlign="middle"
             maxDisplayedLines="1"
             left="18" right="0" top="3" bottom="3" 
    verticalCenter="2" visible="false"/>
</s:SparkSkin>

Renderers class for the individual items that comes with a normal checkbox - CustomCheckBoxRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
     xmlns:s="library://ns.adobe.com/flex/spark" 
     xmlns:mx="library://ns.adobe.com/flex/mx" 
     implements="spark.components.gridClasses.IGridItemRenderer"
     width="100%">
 
 <fx:Script>
  <![CDATA[
   import events.CustomCheckBoxEvents;
   /** 
    * Upon setting the data into the renderer, we need to
    * change the state of the checkbox.
    */
   override public function set data(value:Object):void
   {
    super.data = value;
    chk.selected = data[this.column.dataField]
   } 
   
   /**
    * Upon cliking on the check box, we need to dispatch
    * some events.
    */   
   protected function chk_clickHandler(event:MouseEvent):void
   {
    event.stopImmediatePropagation();
    if(chk.selected)
    {
     this.dispatchEvent(
      new CustomCheckBoxEvents(
       CustomCheckBoxEvents.SELECT_AN_ITEM,
      this.data));
    }else{
     
     this.dispatchEvent(
      new CustomCheckBoxEvents(
       CustomCheckBoxEvents.UNSELECT_AN_ITEM,
      this.data));
    }
   }
   
  ]]>
 </fx:Script>
 <s:VGroup verticalAlign="middle" 
     horizontalAlign="center"
     width="100%" 
     height="100%">
  <s:CheckBox id="chk"
     click="chk_clickHandler(event)"/>
 </s:VGroup>
</s:GridItemRenderer>

CSS file for the application - ../css/style.css
/* CSS file */
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

.customCheckBox{
 skinClass: ClassReference("skins.CustomCheckBoxSkin");
}
* Click here for the demo shown in this post.
  (Try changing some of the checkbox to reflect the change of state in the topmost
  checkbox.)
^ Click here for the source files for the demo.

No comments:

Post a Comment