アーカイブ

どう書く? itemRenderer (ComboBox)

前回のエントリーに引き続き、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 プロパティにセットされた値を、総当たりでマッチングさせます。(賢い方法ではありませんが)