kagamihogeの日記

kagamihogeの日記です。

DataGrid から List へのドラッグ&ドロップ時に型を保持したままコピー

DataGrid や List など ListBase 系コントロールは、dragEnabled, dropEnabled, (dragMoveEnabled )プロパティを設定してやるだけで、要素のドラッグ&ドロップが可能になる。実際にはドラッグ&ドロップ時に細かい制御を実装することになることが多いだろうけど、ただ単に見た目が移動 or コピーしてくれればいいだけなら楽チンである。

@IT のリンク先にもうちょっと詳しいサンプルコードと解説載ってるけど、一応こんな感じ。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" applicationComplete="init()">
    <mx:Script>
        <![CDATA[
            import gridlistdragdrop.HogeDto;
            import mx.collections.ArrayCollection;
            [Bindable]
            private var dataGridDatas:ArrayCollection = new ArrayCollection();
            
            [Bindable]
            private var listDatas:ArrayCollection = new ArrayCollection();
            
            private function init():void {
                dataGridDatas.addItem(new HogeDto("aaa0", "bbb0", "ccc0"));
                dataGridDatas.addItem(new HogeDto("aaa1", "bbb1", "ccc1"));
                dataGridDatas.addItem(new HogeDto("aaa2", "bbb2", "ccc2"));
            }
        ]]>
    </mx:Script>
    <mx:DataGrid id="dataGrid" dataProvider="{dataGridDatas}" dragEnabled="true">
        <mx:columns>
            <mx:DataGridColumn dataField="a" />
            <mx:DataGridColumn dataField="b" />
            <mx:DataGridColumn dataField="c" />
        </mx:columns>
    </mx:DataGrid>
    <mx:List id="list" dataProvider="{listDatas}" labelField="a" dropEnabled="true">
    </mx:List>
</mx:Application>
package gridlistdragdrop
{
    public class HogeDto
    {
        public var a:String;
        public var b:String;
        public var c:String;
        public function HogeDto(a:String, b:String, c:String)
        {
            this.a = a;
            this.b = b;
            this.c = c;
        }

    }
}

ただ、コレだと困ることがひとつ起きた。DataGrid から List へのコピー時に型情報が失われてしまう。もう少し正確に言うと、ドラッグ元のインスタンスをもとに、同じプロパティ名を持つ Object インスタンスが作られる。つまり、インスタンス単位でコピーを行っている。

別にそれ自体は Flex コンポーネントの仕様ってことでいい。だけど、BlazeDS で通信してると DTO オブジェクトをそのまんま DataGrid 等に放り込む。なので、DataProvider はその DTO の型でいてもらったほうが何かと便が良い。

しかし、ドラッグ元(上のサンプルだと DataGrid)の dragMoveEnabled を true にすると型は維持される。コピーでは型が Object になるのに、移動だと型はそのまま。この差は一体何なんだ、ってことで Listbase#dragDropHandler を覗いてみた。

for (i = items.length - 1; i >= 0; i--)
{
    if (event.action == DragManager.COPY)
    {
        collectionIterator.insert(copyItemWithUID(items[i]));
    }
    else if (event.action == DragManager.MOVE)
    {
        collectionIterator.insert(items[i]);
    } 
}

これを見ると、どうもコピーの時はドラッグ元のインスタンスをもとに新しいインスタンス作るのに対して、移動の時は単にドラッグ元のインスタンスをドロップ先に挿入してるだけのようだ。

ってことは、リスト側のドロップ時に event.action を DragManager.MOVE にするというややチョンボ気味のことをしてやれば良さそう。

private function listDragDropHandler(event:DragEvent):void {
    event.action = DragManager.MOVE;
}

<mx:List ... dragDrop="listDragDropHandler(event)">

型情報維持してコピーができた。ただ、これだとドラッグ元とドロップ元が同じインスタンスを指すようになる。今回の俺のケースではむしろ同じインスタンスさしてる方がありがたかったので良かったけど。インスタンス単位でもコピーして欲しい場合は一工夫必要になりますね。