AdvancedDataGridにコンテキストメニューを開いた位置を取得したいときがあるのだが、なかなかうまい方法がなかった。
itemRollOverが発生した時に行と列を保存しておくという手法がちらほら書かれているが、試してみたところうまく機能しない場合があった。
稀にロールオーバーのイベントが飛んで来ずに、マウスのポイントしている位置とコンテキストメニューを開いた時に取得できる位置がずれてしまう。
そこで別の方法を探すことにしたのだが、なかなか良さそうな方法を見つけた。
AdavancedDataGridはデフォルトの状態で、マウスがポイントしている行やヘッダの背景色を変更する。
この背景色を変える処理を参考にして、ポイントしている位置を取得すればいい。
AdvancedDataGridはソースコードが公開されているので、mouseOverHandler()の実装を参考にして
うまくマウスのポイント位置から行と列を取得することができた。

この下記のサンプルだとコンテキストメニューのアイテムをクリックしたときに保存された行と列の情報にアクセスしているが
コンテキストメニューを開いた瞬間のマウスポイント位置のデータをうまく取得できる。
どうやら、コンテキストメニューを開いたあとはAdvancedDataGridのmouseOverHandler()が呼び出されないらしい。
もしメニューが開いたあともmouseOverHandler()が呼ばれる場合はContextMenuEvent.MENU_SELECTの発生時に
データを他に逃がす処理などを入れなければならなかったが、その必要はなくシンプルに実装できた。

<?xml version="1.0" encoding="utf-8"?>
<mx:AdvancedDataGrid 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="advanceddatagrid1_creationCompleteHandler(event)">
	<fx:Declarations>
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
			import mx.controls.listClasses.IDropInListItemRenderer;
			import mx.controls.listClasses.IListItemRenderer;
			import mx.events.FlexEvent;

			[Bindable]
			public var mouseOveredHeaderColumn:AdvancedDataGridColumn;
			[Bindable]
			public var mouseOveredColumn:AdvancedDataGridColumn;
			[Bindable]
			public var mouseOveredRow:int = -1;

			override protected function mouseOverHandler(event:MouseEvent):void {
				mouseOveredHeaderColumn = null;
				mouseOveredColumn = null;
				mouseOveredRow = -1;

				var item:IListItemRenderer = mouseEventToItemRenderer(event);
				var dropInItem:IDropInListItemRenderer;
				if (item) {
					dropInItem = item as IDropInListItemRenderer;
				}

				var colNum:int;
				if (dropInItem && dropInItem.listData) {
					colNum = dropInItem.listData.columnIndex;
				}
				if (dropInItem
					&& isHeaderItemRenderer(item)
					&& sortableColumns
					&& colNum != -1
					&& columns[colNum].sortable
					&& Object(item).hasOwnProperty("mouseEventToHeaderPart") )
				{
					var headerPart:String = Object(item).mouseEventToHeaderPart(event);
					mouseOveredHeaderColumn = item.data as AdvancedDataGridColumn;
				}
				else if ( item != null && !isHeaderItemRenderer(item) )
				{
					mouseOveredColumn = this.columns[colNum] as AdvancedDataGridColumn;
					var pt:Point = itemRendererToIndices(item);
					mouseOveredRow = pt.y;
				}
				super.mouseOverHandler(event);
			}

			protected function advanceddatagrid1_creationCompleteHandler(event:FlexEvent):void
			{
				var item:ContextMenuItem = new ContextMenuItem("くりっくみー");
				item.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, clickListener);
				var cmenu:ContextMenu = new ContextMenu();
				cmenu.customItems.push(item);
				this.contextMenu = cmenu;
			}

			protected function clickListener(event:ContextMenuEvent):void {
				var str:String = "";
				str += "row:" + mouseOveredRow + "  ";
				if ( mouseOveredColumn ) {
					str += "column:" + mouseOveredColumn.headerText;
				}
				if ( mouseOveredHeaderColumn ) {
					str += "header:" + mouseOveredHeaderColumn.headerText;
				}
				Alert.show(str);
			}

		]]>
	</fx:Script>
</mx:AdvancedDataGrid>

AdvancedDataGridにXMLをdataProviderとして与えてツリー表示をする場合などに、HierarchicalDataを使います。
HierarchicalData#childrenFiedlで子ノードを格納する要素名を指定できるのですが、特に表示用に作ったわけでもないXMLである場合、子ノードを
格納する要素の名前が同じであるとは限りません。この場合、childrenFieldでは対応できなくなります。
そこで、子ノードの判定を行う関数を指定できるIHierarchicalDataの実装があると便利です。

というわけで作ってみました。コンストラクタで判定関数を指定できるようになっています。
あと、Xmlに特化している分、HierarchicalDataより高速に動くというメリットもあります。(たぶん)

import flash.events.EventDispatcher;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.collections.IHierarchicalData;

public class XmlHierarchicalData extends EventDispatcher implements IHierarchicalData
{
	private var _source:XMLList;
	private var getChildrenFieldName:Function;

	public function XmlHierarchicalData(value:XMLList, childrenNameFunc:Function) {
		source = value;
		getChildrenFieldName = childrenNameFunc;
	}

	public function get source():XMLList
	{
		return _source;
	}

	public function set source(value:XMLList):void
	{
		_source = value;

		var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
		event.kind = CollectionEventKind.RESET;
		dispatchEvent(event);
	}

	public function canHaveChildren(node:Object):Boolean
	{
		if (node == null) {
			return false;
		}
		var list:XMLList = getChildren(node) as XMLList;
		return list.length() > 0;
	}

	public function hasChildren(node:Object):Boolean
	{
		if (node == null) {
			return false;
		}
		var children:XMLList = XMLList(getChildren(node));
		return children.length() > 0;
	}

	public function getChildren(node:Object):Object
	{
		if ( node == null ) {
			return null;
		}
		var xml:XML = XML(node);
		var name:String = String(xml.localName());
		var childrenField:String = getChildrenFieldName(xml) as String;
		return xml.child(childrenField).*;
	}

	public function getData(node:Object):Object
	{
		return node;
	}

	public function getRoot():Object
	{
		return _source;
	}
}