Here is my own version inspired by James' one. You can enable / disable tristate to get a classic checkbox with the same component, and the mechanism used is a bit different, with less boilerplate. Embedded picture is here.
Use at your own risks ! :)
<?xml version="1.0" encoding="utf-8"?>
<s:Group 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="init()">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
// STATES :
/** The intermediate state value. */
public static const INTERMEDIATE:int = -1;
/** The unchecked state value. */
public static const UNCHECKED:int = 0;
/** The checked state value. */
public static const CHECKED:int = 1;
// TRISTATE :
private var _isTristate:Boolean;
/** Whether this checkbox is tristate or a normal checkbox. */
[Bindable] public function get isTristate():Boolean { return _isTristate; }
public function set isTristate(value:Boolean):void {
_isTristate = value;
if(!_isTristate && state == INTERMEDIATE)
state = UNCHECKED;
}
// LABEL :
/** The checkbox label. */
[Bindable] public var label:String;
// STATE CHANGE :
private var _state:int;
/** The current state of the box. */
public function get state():int { return _state; }
public function set state(value:int):void {
_state = value;
switch(_state) {
case INTERMEDIATE:
if(!_isTristate)
throw new Error("Setting state to INTERMEDIATE on a non tristate checkbox !");
tristateImg.visible = true;
checkbox.selected = false;
break;
case UNCHECKED:
tristateImg.visible = false;
checkbox.selected = false;
break;
case CHECKED:
tristateImg.visible = false;
checkbox.selected = true;
break;
}
}
/**
* Simply sets the state according to the current state.
*/
protected function changeState(ev:Event):void {
if(isTristate) ev.preventDefault();
switch(state) {
case INTERMEDIATE: state = CHECKED; break;
case UNCHECKED: state = isTristate ? INTERMEDIATE : CHECKED; break;
case CHECKED: state = UNCHECKED; break;
}
}
/**
* Initial state.
*/
protected function init():void {
state = UNCHECKED;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:layout>
<s:BasicLayout />
</s:layout>
<s:CheckBox id="checkbox" label="{label}"
change="changeState(event)"/>
<s:Image id="tristateImg"
mouseEnabled="false" mouseChildren="false"
source="@Embed('assets/icons/tristate_checkbox.png')"
x="2" y="5" />
</s:Group>