カスタムデータ記述子の作成

階層データが DefaultDataDescriptor クラスでサポートされる形式に適合しない場合、たとえばデータが子フィールドを使用しないオブジェクト内にある場合、カスタムデータ記述子を記述し、Tree コントロールの dataDescriptor プロパティにカスタムデータ記述子を指定できます。カスタムデータ記述子は、ITreeDataDescriptor インターフェイスのすべてのメソッドを実装する必要があります。

次の例は、カスタムデータ記述子の作成方法を示します。ここでは Tree コントロールを使用しています。このデータ記述子は、ネストされた ArrayCollection オブジェクトから成るデータプロバイダを適切に処理します。

次の例の MyCustomTreeDataDescriptor クラスは ITreeDataDescriptor インターフェイスのみを実装しているため、Tree コントロールはサポートしますが、メニューコントロールはサポートしません。このカスタムクラスは、子フィールドが ArrayCollection または Object であるツリーノードをサポートします。ノードの子を取得するとき、子オブジェクトが ArrayCollection の場合には、その子オブジェクトを返します。それ以外の場合は、子オブジェクトを ArrayCollection でラップしてから返します。ノードを追加するときは、子フィールドのタイプによって異なるメソッドを使用します。

package myComponents
// myComponents/MyCustomTreeDataDescriptor.as
{
import mx.collections.ArrayCollection;
import mx.collections.CursorBookmark;
import mx.collections.ICollectionView;
import mx.collections.IViewCursor;
import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;
import mx.controls.treeClasses.*;

public class MyCustomTreeDataDescriptor implements ITreeDataDescriptor
{

    // The getChildren method requires the node to be an Object
    // with a children field.
    // If the field contains an ArrayCollection, it returns the field
    // Otherwise, it wraps the field in an ArrayCollection.
    public function getChildren(node:Object,
        model:Object=null):ICollectionView
    {
        try
        {
            if (node is Object) {
                if(node.children is ArrayCollection){
                    return node.children;
                }else{
                    return new ArrayCollection(node.children);
                }
            }
        }
        catch (e:Error) {
            trace("[Descriptor] exception checking for getChildren");
        }
        return null;
    }

    // The isBranch method simply returns true if the node is an
    // Object with a children field.
    // It does not support empty branches, but does support null children
    // fields.
    public function isBranch(node:Object, model:Object=null):Boolean {
        try {
            if (node is Object) {
                if (node.children != null)  {
                    return true;
                }
            }
        }
        catch (e:Error) {
            trace("[Descriptor] exception checking for isBranch");
        }
        return false;
    }

    // The hasChildren method Returns true if the node actually has children. 
    public function hasChildren(node:Object, model:Object=null):Boolean {
        if (node == null) 
            return false;
        var children:ICollectionView = getChildren(node, model);
        try {
            if (children.length > 0)
                return true;
        }
        catch (e:Error) {
        }
        return false;
    }
    // The getData method simply returns the node as an Object.
    public function getData(node:Object, model:Object=null):Object {
        try {
            return node;
        }
        catch (e:Error) {
        }
        return null;
    }

    // The addChildAt method does the following:
    // If the parent parameter is null or undefined, inserts
    // the child parameter as the first child of the model parameter.
    // If the parent parameter is an Object and has a children field,
    // adds the child parameter to it at the index parameter location.
    // It does not add a child to a terminal node if it does not have
    // a children field.
    public function addChildAt(parent:Object, child:Object, index:int, 
            model:Object=null):Boolean {
        var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
        event.kind = CollectionEventKind.ADD;
        event.items = [child];
        event.location = index;
        if (!parent) {
            var iterator:IViewCursor = model.createCursor();
            iterator.seek(CursorBookmark.FIRST, index);
            iterator.insert(child);
        }
        else if (parent is Object) {
            if (parent.children != null) {
                if(parent.children is ArrayCollection) {
                    parent.children.addItemAt(child, index);
                    if (model){
                        model.dispatchEvent(event);
                        model.itemUpdated(parent);
                    }
                    return true;
                }
                else {
                    parent.children.splice(index, 0, child);
                    if (model)
                        model.dispatchEvent(event);
                    return true;
                }
            }
        }
        return false;
    }

    // The removeChildAt method does the following:
    // If the parent parameter is null or undefined, removes
    // the child at the specified index in the model.
    // If the parent parameter is an Object and has a children field,
    // removes the child at the index parameter location in the parent.
    public function removeChildAt(parent:Object, child:Object, index:int, model:Object=null):Boolean
    {
        var event:CollectionEvent = new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
        event.kind = CollectionEventKind.REMOVE;
        event.items = [child];
        event.location = index;

        //handle top level where there is no parent
        if (!parent)
        {
            var iterator:IViewCursor = model.createCursor();
            iterator.seek(CursorBookmark.FIRST, index);
            iterator.remove();
            if (model)
                model.dispatchEvent(event);
            return true;
        }
        else if (parent is Object)
        {
            if (parent.children != undefined)
            {
                parent.children.splice(index, 1);
                if (model) 
                    model.dispatchEvent(event);
                return true;
            }
        }
        return false;
    }

}
}

次の例では、MyCustomTreeDataDescriptor を使用して、階層的なネストされた ArrayCollection とオブジェクトを処理します。ボタンがクリックされると、データ記述子の addChildAt() メソッドを呼び出してツリーにノードを追加します。通常は addChildAt() メソッドを直接使用することはありません。その代わりに、Tree またはメニューコントロールのメソッドを呼び出します。これらのメソッドは内部でデータ記述子メソッドを使用して、データプロバイダを変更します。

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- dpcontrols\CustDataDescriptor.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="initCollections()">

    <mx:Script>
        <![CDATA[
            import mx.collections.*;
            import mx.controls.treeClasses.*;
            import myComponents.*;
    
            //Variables used to construct the ArrayCollection data provider
            //First top-level node and its children.
            public var nestArray1:Array = [
                {label:"item1", children: [
                    {label:"item1 child", children:     [
                        {label:"item 1 child child", data:"child data"}
                    ]}
                ]}
            ]; 
            //Second top-level node and its children.
            public var nestArray2:Array = [
                {label:"item2", children: [
                    {label:"item2 child", children: [
                        {label:"item 2 child child", data:"child data"}
                    ]}
                ]}
            ];
            //Second top-level node and its children.
            public var nestArray3:Array = [
                {label:"item3", children: [
                    {label:"item3 child", children: [
                        {label:"item 3 child child", data:"child data"}
                    ]}
                ]}
            ]; 
            //Variable for the tree array.
            public var treeArray:Array
            //Variables for the three Array collections that correspond to the 
            //top-level nodes.
            public var col1:ArrayCollection;
            public var col2:ArrayCollection;
            public var col3:ArrayCollection;
            //Variable for the ArrayCollection used as the Tree data provider.
            [Bindable]
            public var ac:ArrayCollection;
            
            //build the ac ArrayCollection from its parts.
            public function initCollections():void{
                // Wrap each top-level node in an ArrayCollection.
                col1 = new ArrayCollection(nestArray1);
                col2 = new ArrayCollection(nestArray2);
                col3 = new ArrayCollection(nestArray3);
                // Put the three top-level node ArrayCollections in the treeArray.
                treeArray = [
                    {label:"first thing", children: col1},
                    {label:"second thing", children: col2},
                    {label:"third thing", children: col3},
                ]; 
                //Wrap the treeArray in an ArrayCollection.
                ac = new ArrayCollection(treeArray);
            }

            // Adds a child node as the first child of the selected node,
            // if any. The default selectedItem is null, which causes the
            // data descriptor addChild method to add it as the first child
            // of the ac ArrayCollection.
            public function clickAddChildren():void {
                var newChild:Object = new Object();
                newChild.label = "New Child";
                newChild.children = new ArrayCollection();
                tree.dataDescriptor.addChildAt(tree.selectedItem, newChild, 0, ac);
            }

        ]]>
    </mx:Script>

    <mx:Tree width="200" id="tree" dataProvider="{ac}" 
        dataDescriptor="{new MyCustomTreeDataDescriptor()}"/>    
    <mx:Button label="add children" click="clickAddChildren()"/>
</mx:Application>

Flex 2.01