Listing 1: RadioButtonGroupBox.as

// RadioButtonGroupBox.as
package com.theriabook.containers
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import mx.containers.Box;
	import mx.core.IDataRenderer;
	import mx.controls.dataGridClasses.DataGridListData;
	import mx.controls.listClasses.BaseListData;
	import mx.controls.listClasses.IDropInListItemRenderer;
	import mx.controls.RadioButton;
	import mx.controls.RadioButtonGroup;
	import mx.events.FlexEvent;
	import mx.core.mx_internal;
	use namespace mx_internal;

	public class RadioButtonGroupBox extends Box implements  IDataRenderer,  IDropInListItemRenderer
	{
		private var group:RadioButtonGroup=null ;

		public function RadioButtonGroupBox() {
			super();
			verticalScrollPolicy = "off";
			horizontalScrollPolicy = "off";

			group = new RadioButtonGroup();
			group.addEventListener(Event.CHANGE,
				function (event:Event):void { 
					value = event.target.selectedValue;
					dispatchEvent(event);
				}
			);
		}

		override public function addChild(child:DisplayObject):DisplayObject {
			if (child is RadioButton) {
				(child as RadioButton).group = group;
				group.addInstance(child as RadioButton);
			}
			return super.addChild(child);
		}

		override public function set data(item:Object):void	{
			super.data = item;
			if( item!=null ) {
				group.selectedValue = item[DataGridListData(listData).dataField];
			}
		}
 
		[Bindable("valueCommit")]
		[Bindable("change")]
		public function get text():Object {
			return value;
		}

		public function set text(v:Object) : void {
			value = v;
		}

		[Bindable("valueCommit")]
		[Bindable("change")]
		[Inspectable(category="General")]
		public function get value():Object {
			return group.selectedValue;
		}
		public function set value(v:Object) : void {
			group.selectedValue = v;
			if (listData) {
				data[DataGridListData(listData).dataField] = group.selectedValue;
			}
			dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
		}
	
		private var _listData:BaseListData=null;
		public function get listData():BaseListData	{
			return _listData;
		}
		public function set listData(value:BaseListData):void	{
			_listData = value;
		}

		private var _options:Array=null;
		public function set options(opt:Array):void {
			var i:int;
			if (_options!=null) {
				for (i=0; i<_options.length; i++) {
					var child:RadioButton = group.getRadioButtonAt(i);
					removeChild(child);
				}

			}
			_options=opt;

			for (i= 0; i < opt.length; i++) {
				var rb:RadioButton = new RadioButton();
				rb.label = opt[i].label;
				rb.value = opt[i].data;
				addChild(rb);
			}
		} 
	}
}

Listing 2: RadioButtonGroupBoxStandaloneDemo.mxml

<?xml version="1.0" encoding="utf-8"?>
<!-- RadioButtonGroupBoxStandaloneDemo.mxml -->
<mx:Application  xmlns:mx="http://www.adobe.com/2006/mxml" 
xmlns:fx="http://www.theriabook.com/2006" 
layout="vertical">
				<mx:Text text="Selected value is: {rbgb.value}"/>
				<fx:RadioButtonGroupBox id="rbgb" value="L" direction="vertical">
					<fx:options>
						<mx:Array id="options">
							<mx:Object data="A" label="Active"/>
							<mx:Object data="T" label="Terminated"/>
							<mx:Object data="L" label="On leave"/>			
						</mx:Array>
					</fx:options>
					<mx:HRule/>
					<mx:RadioButton value="R" label="Retired"/>
				</fx:RadioButtonGroupBox>
</mx:Application>

Listing 3: RadioButtonGroupHBox.as
 
// RadioButtonGroupHBox.as
package com.theriabook.containers
{
	public class RadioButtonGroupHBox extends RadioButtonGroupBox {
		public function RadioButtonGroupHBox() {
			super();
			direction = "horizontal";
			setStyle("paddingLeft", "5");
		}
	}
}

Listing 4: RadioButtonGroupHBoxDemo.mxml

<?xml version="1.0" encoding="utf-8"?>
<!-- RadioButtonGroupHBoxDemo.mxml -->
<mx:Application  xmlns:mx="http://www.adobe.com/2006/mxml" 
xmlns:fx="http://www.theriabook.com/2006" 
layout="vertical">
	<fx:DataGrid  id="dg" creationComplete="dg.fill();dg.selectedIndex=0;" editable="true"
	destination="com_theriabook_composition_EmployeeDAO" method="getEmployees"
	>
		<fx:columns>
			<mx:Array>
				<mx:DataGridColumn    dataField="EMP_LNAME" headerText="Last Name"  />
				<mx:DataGridColumn    dataField="EMP_FNAME" headerText="First Name" /> 
				<fx:DataGridColumn dataField="STATUS" width="270" headerText="Status" rendererIsEditor="true" 
				itemRenderer="com.theriabook.containers.RadioButtonGroupHBox">
					<fx:options>
						<mx:Array id="options">
							<mx:Object data="A" label="Active"/>
							<mx:Object data="T" label="Terminated"/>
							<mx:Object data="L" label="On leave"/>
						</mx:Array>
					</fx:options>
				</fx:DataGridColumn>
			</mx:Array>
		</fx:columns>
	</fx:DataGrid>
</mx:Application>


Listing 5: StandardDynamicStyleDemo.mxml

<?xml version="1.0" encoding="utf-8"?>
<!-- StandardDynamicStyleDemo.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"  layout="vertical" 
xmlns:fx="http://www.theriabook.com/2006">
	<mx:HBox >
		<mx:Button label="Increase" 
			click="dg.selectedItem.SALARY+=10000.00;
			       dg.dataProvider.itemUpdated(dg.selectedItem);" />
		<mx:Button label="Decrease" 
			click="dg.selectedItem.SALARY-=10000.00;
			       dg.dataProvider.itemUpdated(dg.selectedItem,'SALARY');" />
	</mx:HBox>	<fx:DataGrid  id="dg" creationComplete="dg.fill();dg.selectedIndex=0;"
	destination="com_theriabook_composition_EmployeeDAO" method="getEmployees"
	>
		<fx:columns>
			<mx:Array>
				<mx:DataGridColumn    dataField="EMP_LNAME"   />
				<mx:DataGridColumn    dataField="EMP_FNAME"  />
				<mx:DataGridColumn    dataField="SALARY" textAlign="right">
					<mx:itemRenderer>
						<mx:Component>
							<mx:Label 
								color="{data.SALARY>50000?255*256*256:255*256}"
							/>
						</mx:Component>
					</mx:itemRenderer>
				</mx:DataGridColumn>
			</mx:Array>
		</fx:columns>
	</fx:DataGrid>

</mx:Application>


Listing 6: Label.as

// Label.as (theriabook.swc)
package com.theriabook.controls
{
	import mx.controls.Label;

	[Style(name="backgroundAlpha", type="Number", inherit="no")]
	[Style(name="backgroundColor", type="uint", format="Color", inherit="no")]

	dynamic public class Label extends mx.controls.Label
	{
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void 	{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
	 		if (getStyle("backgroundColor")){
				graphics.clear();
				graphics.beginFill(getStyle("backgroundColor"), getStyle("backgroundAlpha"));
				graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
	 			graphics.endFill();
	 		}
		}

	}
}


Listing 7: UIClassFactory.as, the first version

//UIClassFactory.as
package com.theriabook.util
{
	import mx.core.IFactory;
	import mx.core.ClassFactory;
	import mx.events.FlexEvent;
	import mx.controls.dataGridClasses.DataGridColumn;

	public class UIClassFactory implements IFactory
	{
		private var wrappedClassFactory : ClassFactory;

		public function set properties(v:Object):void	{
			wrappedClassFactory.properties = v;
		}
		public function get properties():* {
			return wrappedClassFactory.properties ;
		}
		public function UIClassFactory( cf:ClassFactory ) {
			wrappedClassFactory = cf;
		}

		public function newInstance():* {
			var obj:* = wrappedClassFactory.newInstance();
			obj.addEventListener(FlexEvent.DATA_CHANGE, onDataChange);
			return obj;
		}

		private function onDataChange(event:FlexEvent):void{

			var renderer:Object = event.currentTarget;

// In the default DataGridItemRenderer both data and listData are 
//    Bindable("dataChange")]
			// We want to skip assinments to listData
			if (renderer.data is mx.controls.dataGridClasses.DataGridColumn) return;

			// Act only on 'dynamic style' columns
			if (renderer.styleName && renderer.styleName.hasOwnProperty("runtimeStyles")) {
				var runtimeStyles:Object = renderer.styleName["runtimeStyles"];
				for (var style:String in runtimeStyles) {
					if ( runtimeStyles[style] is Function ) {
						var functionObject : Function = runtimeStyles[style];
						renderer.setStyle(style, functionObject(renderer.data));
					}
					else
						renderer.setStyle(style, runtimeStyles[style]);
				}
				renderer.invalidateDisplayList();
			}

		}
	}
}


Listing 8: DataGridColumn.as

// DataGridColumn.as
package com.theriabook.controls.dataGridClasses{
	import com.theriabook.util.UIClassFactory;
	import mx.controls.dataGridClasses.DataGridColumn;
	import mx.core.ClassFactory;
	import mx.core.IFactory;

	public class DataGridColumn  extends mx.controls.dataGridClasses.DataGridColumn {

		public var runtimeStyles:Object = null;
		public var runtimeProperties:Object = null;
 		public function set extendedProperties(val:Object) :void {
 			if (itemRenderer)
				itemRenderer["properties"] = val;
		}

		override public function set itemRenderer( val : IFactory ) : void {
			super.itemRenderer = new UIClassFactory(val as ClassFactory);
		}

		public function  set formatString( fs :String ) : void{
			formatData = fs;
		}

		public function  set formatData( fd :Object ) : void{
			FormattingManager.setFormat(this, fd);
		}
	}
}


Listing 9: RuntimeStyleDemo.mxml

<?xml version="1.0" encoding="utf-8"?>
<!-- RuntimeStylesDemo.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"  layout="vertical" 
xmlns:fx="http://www.theriabook.com/2006">
	<mx:Script>
		<![CDATA[
			import com.theriabook.composition.dto.EmployeeDTO;
			private var linkage:com.theriabook.composition.dto.EmployeeDTO
			private function computedFontWeight(item:Object):String {
				return (item.SALARY>55000)?'bold':'normal';
			}
		]]>
	</mx:Script>
	<mx:Button label="Increase" 
		click="dg.selectedItem.SALARY+=10000.00;
		       dg.dataProvider.itemUpdated(dg.selectedItem);" />
	<mx:Button label="Decrease" 
		click="dg.selectedItem.SALARY-=10000.00;
		       dg.dataProvider.itemUpdated(dg.selectedItem,'SALARY');" />
	<fx:DataGrid  id="dg" creationComplete="dg.fill();dg.selectedIndex=0;"
		destination="com_theriabook_composition_EmployeeDAO" method="getEmployees"
		>
		<fx:columns>
			<mx:Array>
				<mx:DataGridColumn    dataField="EMP_LNAME"   />
				<mx:DataGridColumn    dataField="EMP_FNAME"  />
				<fx:DataGridColumn    dataField="SALARY" textAlign="right" formatString="money">
					<fx:runtimeStyles>
							<mx:Object 
								backgroundColor="{function(item:Object):String {return 
										(item.SALARY>50000)?'red':'green';}}"
							    fontWeight="{computedFontWeight}"
							/>
					</fx:runtimeStyles>
				</fx:DataGridColumn>
			</mx:Array>
		</fx:columns>
	</fx:DataGrid>

</mx:Application>


Listing 10: NumericInput.as, first version

// NumericInput.as
package com.theriabook.controls
{
	import mx.controls.TextInput;
	import flash.events.Event;

	public class NumericInput extends TextInput
	{
        public function NumericInput() {
			super();
        		addEventListener(flash.events.TextEvent.TEXT_INPUT, onTextInput);
        }

	private function onTextInput(event:flash.events.TextEvent):void {
  		// TODO Find out number separators from the locale settings
  		var re:RegExp = 
  		new RegExp("[^0-9,.-]", "i");
  		var illegalCharacterFound:Boolean = re.test(event.text);
  		if (illegalCharacterFound) {
			event.preventDefault();
  		}
  	}
   }
}


Listing 11: NumericInputDemo.mxml

When you run it, try to type anything but digits, comma and dot into the
<?xml version="1.0" encoding="utf-8"?>
<!--NumericInputDemo.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	xmlns:fx="http://www.theriabook.com/2006" 
	layout="vertical"
	 creationComplete="onCreationComplete();">
	 <mx:Script>
	 	<![CDATA[
	 		import mx.collections.*;
	 		[Bindable]
	 		private var collection:ArrayCollection;
	 		private function onCreationComplete():void {
	 			collection = new ArrayCollection ([
	 				new NumberScope(124000.00),
	 				new NumberScope(Number.NaN),
	 				new NumberScope(null),
	 				new NumberScope(undefined)
	 			]);
	 		}

	 	]]>
	 </mx:Script>

	<fx:DataGrid id="dg" dataProvider="{collection}" editable="true" >
		<fx:columns>
		<mx:Array>
				<fx:DataGridColumn dataField="description" headerText="Description" editable="false"/>
				<mx:DataGridColumn dataField="number" headerText="Raw Number" editable="false"/>
				<fx:DataGridColumn dataField="number" headerText="Number" editable="true" editorDataField="value"
				 itemEditor="com.theriabook.controls.NumericInput" formatString="money"/>
		</mx:Array>
		</fx:columns>
	</fx:DataGrid>
</mx:Application>


Listing 12: NumberScope.as, a helper class for NumericInputDemo

//NumberScope.as, helper class for NumericInputDemo.mxml
package {
	public class NumberScope {
		public var number:*;
		public function get description():String {
				if (number===null) return 'null';
				if (number===undefined) return 'undefined';
				return (typeof number);
		}
		public function NumberScope(n:*) {
				number=n;
		}
	}
}

Listing 13: NumericInput.as, the final version

// NumericInput.as
package com.theriabook.controls
{
	import mx.controls.TextInput;
	import flash.events.Event;
	import mx.controls.List;

	public class NumericInput extends TextInput
	{
        import mx.controls.listClasses.BaseListData;
		import mx.events.FlexEvent;
        import mx.controls.dataGridClasses.DataGridListData;

        
        public function NumericInput() {
        	super();
        	addEventListener(flash.events.TextEvent.TEXT_INPUT, onTextInput);
        }

		private var _value:*;
		[Bindable("change")]
		public function set value(v:*):void {
			_value = v;
		  	if ((String(v)=="NaN") || (v==null /*or undefined*/)) {
		  		text="";
		  	} else {
		  		text = String(v as Number);
		  	}
		}

		public function get value():* {
			// Preserve NaN - null - undefined, when nothing has been entered
	  		if (((_value!=null )&& (String(_value)!="NaN")) || (text!="") ) { 
		  		_value = Number(text.replace(/,/g,"")); // deformat first
		  	}
	  		return _value;
		}

	  	public override  function set data(item:Object):void {
			if (listData && listData is DataGridListData) {
		  		var dgListData:DataGridListData = DataGridListData(listData);
		  		value = item[dgListData.dataField];
 			  	dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
 			  	return;
		 	}
		 	super.data = item;
		}

	  	private function onTextInput(event:flash.events.TextEvent):void {
	  		// TODO Find out number separators from the locale settings
	  		var re:RegExp = 	new RegExp("[^0-9,.-]", "i");
	  		var illegalCharacterFound:Boolean = re.test(event.text);
	  		if (illegalCharacterFound) {
				event.preventDefault();
	  		}
	  	}
	}
}