前回のエントリーに引き続き、itemRenderer について書きます。
サンプルは以下の通り。
ItemRendererComboBoxSample (画面)
http://labs.taiga.jp/flex2/ItemRendererComboBoxSample/
ItemRendererComboBoxSample (ソース)
http://labs.taiga.jp/flex2/ItemRendererComboBoxSample/srcview/
まず、ItemRendererComboBoxSample.mxml の <mx:DataGridColumn> タグに着目します。
<!-- カスタム ComboBox -->
<mx:DataGridColumn
headerText = "colmn01"
dataField = "combo"
itemRenderer = "lib.SubComboBox"
rendererIsEditor = "true"
editorDataField = "selectedIndex"
editable = "true"
/>
itemRenderer プロパティ値に、カスタム ComboBox クラスを定義しています。
rendererIsEditor プロパティと editorDataField プロパティを設定している箇所は、前回のカスタム CheckBox と同じです。
そして今回、editorDataField プロパティには、ComboBox のプロパティ名である slectedIndex を定義しています。
次に、SubComboBox.as に着目します。
package lib
{
import flash.events.Event;
import mx.collections.ArrayCollection;
import mx.controls.ComboBox;
import mx.controls.dataGridClasses.DataGridListData;
/**
* SubComboBox
* itemRenderer 用 ComboBox サブクラス
*/
public class SubComboBox extends ComboBox
{
/**
* mValue
* @private
* クラス内部で保持する selectedIndex 値
*/
private var mValue:uint;
/**
* SubComboBox
* コンストラクタ
*/
public function SubComboBox()
{
super();
width = 100;
dataProvider = [ {label:'Label_A', data:10},
{label:'Label_B', data:20},
{label:'Label_C', data:30} ];
addEventListener(Event.CHANGE, changeHandler, false, 0, true);
}
/**
* tmpValue
* @private
* selectedIndex 値(内部保持用)
*/
private function get tmpValue():uint
{
return mValue;
}
/**
* @private
*/
private function set tmpValue(aValue:uint):void
{
mValue = aValue;
invalidateProperties();
}
/**
* data
* @param aValue itemRenderer の値
* @private
* プロパティ ComboBox の selectedIndex 値
*/
override public function set data(aObject:Object):void
{
super.data = aObject;
if (null != dataProvider)
{
selectedIndex = getLabelIndex( data[DataGridListData(listData).dataField] );
}
}
/**
* commitProperties
* @private
* Binding しているプロパティの更新
*/
override protected function commitProperties():void
{
this.selectedIndex = getLabelIndex( data[DataGridListData(listData).dataField] );
super.commitProperties();
}
/**
* getLabelIndex
* @param aValue dataProvider の値
* @return selectedIndex 値
* @private
* 引数と ComboBox 内のデータを評価して、マッチしたら該当インデックスを返却
*/
private function getLabelIndex(aValue:int):uint
{
var lArrayCollection:ArrayCollection = dataProvider as ArrayCollection;
var lLength:Number = lArrayCollection.length;
for (var i:uint = 0; i < lLength; i++)
{
if (lArrayCollection.getItemAt(i).data == aValue)
{
return i;
}
}
return 0;
}
/**
* changeHandler
* @param event Event オブジェクト
* @private
* ComboBox の change イベント
*/
private function changeHandler(event:Event):void
{
var lArrayCollection:ArrayCollection = dataProvider as ArrayCollection;
if (listData)
{
tmpValue = lArrayCollection.getItemAt(this.selectedIndex).data;
data[DataGridListData(listData).dataField] = tmpValue;
}
}
}
}
コンストラクタで ComboBox の label 値と data 値を定義します。
DataGrid をスクロールさせると ComboBox.commitProperties() メソッドが実行されます。
このとき ComboBox の label 値を変更していると初期表示状態に戻ってしまうので、この現象を回避するために ComboBox.commitProperties() メソッドをオーバーライドし、changeHandler() メソッドで保持しておいた data 値をセットさせる処理を追記しています。
DataGrid.dataProvider プロパティにセットする ComboBox の値ですが、ComboBox の data 値のみ使用します。( label は、ただのお飾りということです)
なぜかというと、本来 ComboBox.selectedIndex プロパティには、label 値と data 値を内包したオブジェクト(例: {label:”hoge”, data:”fuga”} )を代入しないと正しく動作しないのですが、DataGrid.dataProvider プロパティに例のようなオブジェクトをセットしてしまうと、DataGird のヘッダをクリックしてソート処理を行ったとき、DataGird がソート対象を判別できず、例外エラーが発生してしまうためです。
そして、正しく動作しない selectedIndex プロパティを正しく動作させるために、getLabelIndex() というメソッドを作成し、ComboBox の data 値と DataGrid.dataProvider プロパティにセットされた値を、総当たりでマッチングさせます。(賢い方法ではありませんが)