Saturday, September 25, 2010

MXDataGridItemRenderer Focus

Author's note - since writing this post I subsequently discovered the "editor" property which does exactly what my "focusChild" property does below. I've left the post intact anyway to help highlight the point and maybe also in the faint hope that somebody from the documentation team at Adobe sees this and does something wild like mentioning this property in their examples...

Once you have switched your existing Flex 3 projects over to trying to use the spark components in Flex 4 you will come across a problem with the new setup for DataGrid ItemRenderers. You will realise that things are now structured differently and you will need to use the MXDataGridItemRenderer class to get your item renderers working again.

You smile a wry smile and bite the bullet, but then something else happens. You realise that things have got all “out of focus”... And by that I mean that tab and mouse focusing doesn't work like they did in Flex 3, and the behaviour is not what you (or your users) would be expecting.

The good thing is the solution is ultimately pretty simple. The code below shows a sub-class of MXDataGridItenRenderer which you can use to get the old focus behaviour back to the way it was. What happens is MXDataGridItemRenderer itself receives the focus, but can't really do anything with it, so you yourself get the job of passing it on to your TexInput, DropDownList, whatever. The way we did it was to add a property called focusChild to our sub-class, as the itemRenderer could conceivably contain any number of custom controls, and this helps to stipulate which one gets the focus when the user mouse-clicks or tabs into the renderer for the given column on the DataGrid.

Our team on the job generally subclass this class, and use the creationComplete event to set the focusChild property to the custom control. The accessors on Data and ListData are there for convenience so that these can be accessed by a subclass (in Flex 3 this complied with the interfaces for IDataRenderer and IListItemRenderer). We also generally override the set data method for setting custom properties on a custom control.



<s:MXDataGridItemRenderer
fx="http://ns.adobe.com/mxml/2009"
mx="library://ns.adobe.com/flex/mx"
s="library://ns.adobe.com/flex/spark"
focusin="gotFocus(event)"
focusenabled="true"
implements="mx.managers.IFocusManagerComponent">
<fx:script>
<!--[CDATA[

import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.BaseListData;
import flash.events.Event;
import mx.managers.IFocusManagerComponent;

protected var _listData:DataGridListData;
protected var focusChild:IFocusManagerComponent;

private function gotFocus(event:Event):void
{
if (focusChild)
{
focusChild.setFocus();
}
}
override public function set data(value:Object):void
{
super.data = value;
}
public override function get data():Object
{
return super.data;
}
override public function get listData():BaseListData
{
return _listData;
}
override public function set listData(value:BaseListData):void
{
_listData = DataGridListData(value);
}
]]-->

</fx:script>
</s:MXDataGridItemRenderer>