Flex Quick Start: Building an advanced user interface

Table of contents


Working with Tree controls

A Tree control is a hierarchical structure of branch and leaf nodes . Each item in a tree is called a node and can be either a leaf or a branch. A branch node can contain leaf or branch nodes, or can be empty (have no children). A leaf node is an end point in the tree. This quick start covers some of the challenges developers frequently encounter when working with Tree controls.

Comparing use of XMLListCollection and ArrayCollection objects

You might wonder whether you should use an XMLListCollection object or an ArrayCollection object as the data provider for a Tree control when working with data that changes dynamically; this includes data returned from a remote data service or data in local objects that are modified at run time. Whether you use an XMLListCollection object or an ArrayCollection object depends on the form of the data.

If you are working with a data source that provides well formed XML and you want to manipulate the data as XML in the Tree control, you should use an XMLListCollection object as the data provider. When working in MXML tags, if the data source is an XMLList object, you should bind that object to the source property of an XMLListCollection object, and then bind the XMLListCollection object to the dataProvider property of the Tree control. Do not bind an XMLList object or XML object directly to the dataProvider property of the Tree control when you expect these objects to change dynamically.

When your data source is the lastResult object of an RPC service call and you want to work with XML data, ensure that the resultFormat property on the RPC component is set to "e4x" ; when you use the E4X result format, the lastResult object is an XMLList object that you can bind to the source property of an XMLListCollection object. Here is an example that uses the E4X result format .

For object data that changes dynamically, use an ArrayCollection object as the Tree control's data provider. When working with MXML tags, you should not bind an Array object directly to the dataProvider property of a Tree control when you expect the Array to change dynamically. Instead, you should bind the Array to the source property of an ArrayCollection object and bind that to the dataProvider property of the Tree control.

When the data source is the lastResult object of an RPC service call and the resultFormat property of the RPC component is set to "object" , you should use the ArrayUtil.toArray() method to ensure that the object is an Array when you bind it to an ArrayCollection object, as the following example shows:

<mx:ArrayCollection

    id="employeeAC"

    source= "{ArrayUtil.toArray(employeeSrv.lastResult.employees.employee)}"/>

Add and remove leaf nodes at run time

You can add and remove leaf nodes from a Tree control at run time. The following example contains code for making these changes. The application initializes with predefined branches and leaf nodes that represent company departments and employees. You can add leaf nodes to one of the branches at run time. You can also remove the predefined leaf nodes and the ones you create yourself at run time.

The XML in this example contains two different element names, department and employee. The Tree control's label function, treeLabel() , determines what text is displayed for these types of elements. It uses E4X syntax to return either the title of a department or the name of an employee. Those values are then used in the addEmployee() and removeEmployee() methods.

To add employees to the Operations department, the addEmployee() method uses E4X syntax to get the Operations department node based on the value of its title attribute and stores it in the dept variable, which is of type XMLList. It then appends a child node to the Operations node by calling dept.appendChild() .

The removeEmployee() method stores the currently selected item in the node variable, which is of type XML. The method calls the node.localName() method to determine if the selected item is an employee node. If the item is an employee node, it is deleted.

Example

<?xml version="1.0" encoding="utf-8"?>

<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml
" viewSourceURL="src/index.html
">


<mx:Script>

<!
[
CDATA[


import
 mx.collections.XMLListCollection;

[
Bindable
]

private
 var
 company:
XML =


<
list>

<
department title=
"Finance"
 code=
"200"
>

<
employee name=
"John H"
/>


<
employee name=
"Sam K"
/>

</
department>

<
department title=
"Operations"
 code=
"400"
>


<
employee name=
"Bill C"
/>

<
employee name=
"Jill W"
/>

</
department>
                    
<
department title=
"Engineering"
 code=
"300"
>


<
employee name=
"Erin M"
/>

<
employee name=
"Ann B"
/>

</
department>
                                
</
list>
;

[
Bindable
]


private
 var
 companyData:
XMLListCollection =
 new
 XMLListCollection(
company.department)
;

private
 function
 treeLabel(
item:
Object)
:
String
{


var
 node:
XML =
 XML(
item)
;
if
(
 node.localName()
 ==
 "department"
 )


return
 node.@title;
else

return
 node.@name;
}

private
 function
 addEmployee()
:
void


{

var
 newNode:
XML =
 <
employee/>
;

                newNode.@name =
 empName.text;
var
 dept:
XMLList =
company.department.(
@title ==
 "Operations"
)
;
if
(
 dept.length()
 >
 0 )
 {



                    dept[
0]
.appendChild(
newNode)
;

                    empName.text =
 ""
;
}

}


private
 function
 removeEmployee()
:
void

{

var
 node:
XML =
 XML(
tree.selectedItem)
;
if
(
 node ==
 null
 )
 return
;
if
(
 node.localName()
 !=
 "employee"
 )
 return
;

var
 children:
XMLList =
 XMLList(
node.parent())
.children()
;
for
(
var
 i:
Number=
0; i <
 children.length()
; i++
)
 {


if
(
 children[
i]
.@name ==
 node.@name )
 {

delete
 children[
i]
;
}


}

}

]]
>

</mx:Script>


<mx:Tree
 id="tree
" top="72
" left="50
" dataProvider="{
companyData}
"

        labelFunction="treeLabel
"

         height="224
" width="179
"/>


<mx:HBox>
        
<mx:Button
 label="Add Operations Employee
" click="addEmployee()
"/>
<mx:TextInput
 id="empName
"/>


</mx:HBox>

<mx:Button
 label="Remove Selected Employee
" click="removeEmployee()
"/>
    
</mx:Application>

Result

<script type="text/javascript"> // <![CDATA[ var props = new Object(); props.swf = "/devnet/flex/quickstart/working_with_tree/swfs/TreeAddRemoveNodes.swf"; props.id = "resultswfTreeAddRemoveNodes"; props.w = "400"; props.h = "380"; props.ver = "9"; props.wmode= "window"; props.menu="true"; var swfo = new SWFObject( props ); registerSWFObject( swfo, "swfTreeAddRemoveNodes" ); // ]]> </script>

To view the full source, right-click the Flex application and select View Source from the context menu.

Add an empty branch node at run time

You can add empty branches to a Tree control at run time. The following example shows how to add branch nodes through the data provider API and through the data descriptor API. Generally, the preferred way to add empty branch nodes is through the data provider API.

The addEmptyBranchDP() method adds an empty branch through the data provider API for a with an XML data provider. This method creates a variable of type XML for a new node and sets the node's isBranch attribute to "true" to create a branch node. The method then calls the Tree control's dataProvider.addItemAt() method to add a new item to the Tree control's data provider.

The addEmptyBranchDP2() method adds an empty branch through the data provider API for a Tree control with an object data provider. This method creates an object with a children property, which makes the isBranch() method return true . The method then calls the Tree control's dataProvider.addItemAt() method to add the new object to the Tree control's data provider.

The addEmptyBranchDD() method adds an empty branch through the data descriptor API. This method creates a variable of type XML for a new Tree node and sets the node's isBranch attribute to "true" to create a branch node. The method then calls the Tree control's dataDescriptor.addChildAt() method to add a new child to the Tree control's data descriptor.

Example

<?xml version="1.0" encoding="utf-8"?>

<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml
" viewSourceURL="src/index.html
">


<mx:Script>

<!
[
CDATA[


[
Bindable
]

private
 var
 dataX:
XML =

<
item label=
"Top"
>


<
item label=
"Child One"
/>

<
item label=
"Child Two"
 />

</
item>
;

[
Bindable
]


private
 var
 dataObj:
Object =
 
[{
label:
"Top"
, children:
 
[


{
label:
"Child One"
}
, {
label:
 "Child Two"
}

]


}]
;

// Adding a branch by going through the Tree control's dataProvider. This is 

            // the preferred method.

            // Toggling the isBranch attribute to true causes DefaultDataDescriptor.isBranch()

            // to return true and the Tree treats the node as a branch. 

            private
 function
 addEmptyBranchDP()
:
void


{

var
 newNode:
XML =
 <
item label=
'Middle'
 isBranch=
"true"
></
item>
;

                xmlBound2Tree.dataProvider.addItemAt(
newNode, 1)
;
}



// For an object graph, the key point is that the children property needs to be defined,

            // even if it is empty.

            // This causes isBranch() to return true and the Tree treats the new node as a branch.

            private
 function
 addEmptyBranchDP2()
:
void

{


var
 newObj:
Object =
 {
label:
"Middle"
, children:
[]}
;

                objGraphBound2Tree.dataProvider.addItemAt(
newObj, 1)
;
}



// Adding a branch by going through the Tree control's dataDescriptor. 

            private
 function
 addEmptyBranchDD()
:
void

{

var
 newNode:
XML =
 <
item label=
'Child 4'
 isBranch=
"true"
></
item>
;

                xmlBound2Tree.dataDescriptor.addChildAt(
dataX, newNode, 2, dataX)
;
}


]]
>

</mx:Script>


<mx:Label
 text="Tree with XML data
"/>

<mx:Tree
 id="xmlBound2Tree
" dataProvider="{
dataX}
" labelField="@label
" showRoot="true
" width="200
"/>


<mx:Button
 label="Add Empty Branch through the dataProvider
" click="addEmptyBranchDP()
;"/>

<mx:Button
 label="Add Empty Branch through the dataDescriptor
" click="addEmptyBranchDD()
;"/>


<mx:Spacer
 height="10
"/>

<mx:Label
 text="Tree with object data
"/>

<mx:Tree
 id="objGraphBound2Tree
" dataProvider="{
dataObj}
" width="200
"/>


<mx:Button
 label="Add Empty Branch through the dataProvider
" click="addEmptyBranchDP2()
;"/>
    
</mx:Application>

<script type="text/javascript"> // <![CDATA[ var props = new Object(); props.swf = "/devnet/flex/quickstart/working_with_tree/swfs/TreeAddEmptyBranch.swf"; props.id = "resultswfTreeAddEmptyBranch"; props.name = "resultswfTreeAddEmptyBranch"; props.w = "400"; props.h = "520"; props.ver = "9"; props.c = "#94AAB5"; props.wmode= "window"; props.menu="true"; var swfo = new SWFObject( props ); registerSWFObject( swfo, "swfTreeAddEmptyBranch" ); // ]]> </script>

To view the full source, right-click the Flex application and select View Source from the context menu.

Open a Tree to a specific node

By default, the Tree control is collapsed when it initializes, and you might be uncertain how to initialize the Tree so that it is expanded with a specific node selected. The following example shows how to do that. In this application, the initTree() method is called after the Tree control is created. This method expands the root node of the Tree control and sets its selectedIndex property to the index number of a specific node.

Example

<?xml version="1.0"?>

<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml
" viewSourceURL="src/index.html
">


<mx:Script>

<!
[
CDATA[

import
 flash.events.*
;
import
 mx.events.*
;
import
 mx.controls.*
;
private
 function
 initTree()
:
void
 {



                XMLTree1.expandItem(
MailBox.getItemAt(
0)
, true
)
;

                XMLTree1.selectedIndex =
 2;
}

]]
>


</mx:Script>


<mx:Tree
 id="XMLTree1
" width="150
" height="170
"

             labelField="@label
" creationComplete="initTree()
;">


<mx:XMLListCollection
 id="MailBox
">

<mx:XMLList>

<node
 label="Mail
" data="100
">


<node
 label="Inbox
" data="70
"/>

<node
 label="Personal Folder
" data="10
">


<node
 label="Business
" data="2
"/>

<node
 label="Demo
" data="3
"/>


<node
 label="Saved Mail
" data="5
" />

</node>

<node
 label="Sent
" data="15
"/>


<node
 label="Trash
" data="5
"/>

</node>

</mx:XMLList>

</mx:XMLListCollection>


</mx:Tree>

</mx:Application>

Result

<script type="text/javascript"> // <![CDATA[ var props = new Object(); props.swf = "/devnet/flex/quickstart/working_with_tree/swfs/TreeOpenToNode.swf"; props.id = "resultswfTreeOpenToNode"; props.name = "resultswfTreeOpenToNode"; props.w = "200"; props.h = "225"; props.ver = "9"; props.c = "#94AAB5"; props.wmode= "window"; props.menu="true"; var swfo = new SWFObject( props ); registerSWFObject( swfo, "swfTreeOpenToNode" ); // ]]> </script>

To view the full source, right-click the Flex application and select View Source from the context menu.

Read an XML document with multiple node names

You can populate a Tree control from an XML document that has multiple node names. The following example shows how to do this. The Tree control's data provider is an XMLListCollection populated from an XMLList that contains folder and Pfolder elements. The Tree control's labelField property is set to the label attribute, which is common to all of the XMLList elements, regardless of the element names.

Example

<?xml version="1.0"?>

<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml
" viewSourceURL="src/index.html
">

<mx:Tree
 id="tree1
" dataProvider="{
MailBox}
" labelField="@label
" showRoot="true
" width="160
"/>


<mx:XMLListCollection
 id="MailBox
" source="{
Folders}
"/>

<mx:XMLList
 id="Folders
">


<folder
 label="Mail
">

<folder
 label="INBOX
"/>

<folder
 label="Personal Folder
">


<Pfolder
 label="Business
" />

<Pfolder
 label="Demo
" />
 
<Pfolder
 label="Saved Mail
" />
 
</folder>


<folder
 label="Sent
" />

<folder
 label="Trash
" />

</folder>


</mx:XMLList>
    
</mx:Application>

Result

<script type="text/javascript"> // <![CDATA[ var props = new Object(); props.swf = "/devnet/flex/quickstart/working_with_tree/swfs/TreeDiffNodeNames.swf"; props.id = "resultswfTreeDiffNodeNames"; props.name = "resultswfTreeDiffNodeNames"; props.w = "275"; props.h = "250"; props.ver = "9"; props.c = "#94AAB5"; props.wmode= "window"; props.menu="true"; var swfo = new SWFObject( props ); registerSWFObject( swfo, "swfTreeDiffNodeNames" ); // ]]> </script>

To view the full source, right-click the Flex application and select View Source from the context menu.

Keep a Tree control open with the data provider is updated

By default, the Tree control collapses when its data provider is updated with new data. The following example shows one way to keep the Tree control expanded when its data provider is updated.

In this application, the changeProvider() method refreshes the data provider when a user clicks on a Button control. Normally, this results in the Tree control collapsing. However, the Tree control's render event handler, the renderTree() method, prevents that from happening. When the changeProvider() method is called, the current state of open items is saved in an object that is assigned to the variable named open . A Boolean variable named refreshData , is set to true when the data is refreshed. The renderTree() method calls the Tree control's invalidateList() method to refresh the Tree rows. It then sets the refreshData property to false and resets the Tree control's openItems property to the open object, which contains the state of open items before the data provider was refreshed.

Example

<?xml version="1.0" encoding="utf-8"?>

<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml
" layout="vertical
" initialize="initTree()
" viewSourceURL="srcview/index.html
">


<mx:Script>

<!
[
CDATA[

[
Bindable
]

public
 var
 open:
Object =
 new
 Object()
;
[
Bindable
]


public
 var
 refreshData:
Boolean =
 false
;
[
Bindable
]

public
 var
 switchObj:
Object =
 new
 Object()
;
[
Bindable
]


public
 var
 firstObj:
Object =
 new
 Object()
;
[
Bindable
]


public
 var
 firstObj1:
Object =
 new
 Object()
;
[
Bindable
]


public
 var
 firstObj2:
Object =
 new
 Object()
;
[
Bindable
]


public
 var
 provider:
String =
 "firstObj"
;

private
 function
 initTree()
:
void


{



                firstObj =
 new
 Object()
;

                firstObj.label =
 "Foods"
;

                firstObj.children =
 new
 Array()
;

                firstObj1.label =
 "Fruits"
;

                firstObj1.children =
 new
 Array()
;

                firstObj2.label =
 "Oranges"
;

                firstObj1.children[
0]
 =
 firstObj2;

                firstObj.children[
0]
 =
 firstObj1;

                switchObj =
 firstObj;
}


public
 function
 changeProvider()
:
void

{


                open =
 SampleTree.openItems;

                refreshData =
 true
;
if
 (
provider ==
 "firstObj"
)


{


                    provider =
 "switchObj"
;

                    SampleTree.dataProvider =
 switchObj;
}

else


{


                    provider =
 "firstObj"
;

                    SampleTree.dataProvider =
 firstObj;
}

}


public
 function
 renderTree()
:
void
{

if
(
refreshData){


// Refresh all rows on next update.

                    SampleTree.invalidateList()
;

                    refreshData =
 false
;

                    SampleTree.openItems =
 open;
// Validate and update the properties and layout

                    // of this object and redraw it, if necessary.

                    SampleTree.validateNow()
;
}


}

]]
>

</mx:Script>

<mx:Tree
 id="SampleTree
" render="renderTree()
" width="250
" dataProvider="{
firstObj}
" labelField="label
" />


<mx:Button
 label="Change Data Provider
" click="changeProvider()
"/>

</mx:Application>

Result

<script type="text/javascript"> // <![CDATA[ var props = new Object(); props.swf = "/devnet/flex/quickstart/working_with_tree/swfs/TreeKeepOpen.swf"; props.id = "resultswfTreeKeepOpen"; props.name = "resultswfTreeKeepOpen"; props.w = "350"; props.h = "240"; props.ver = "9"; props.c = "#94AAB5"; props.wmode= "window"; props.menu="true"; var swfo = new SWFObject( props ); registerSWFObject( swfo, "swfTreeKeepOpen" ); // ]]> </script>

To view the full source, right-click the Flex application and select View Source from the context menu.

Drag and drop items to and from a Tree control

Creating an application that drags and drops items to or from a Tree control can seem daunting, particularly because it can require quite a bit of event handling logic. This section provides two examples that demonstrate techniques for both of these scenarios.

Dragging and dropping from a Tree control

This following example show how to drag and drop items from a Tree control to a DataGrid control. The Tree control's data provider is an XML object. Use the comments that precede the drag and drop event handler methods to guide you through the application.

Example

<?xml version="1.0" encoding="utf-8"?>

<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml
" layout="absolute
">


<mx:Script>

<!
[
CDATA[

import
 mx.controls.Alert;
import
 mx.controls.Label;
import
 mx.controls.List;
import
 mx.collections.ArrayCollection;
import
 mx.core.DragSource;
import
 mx.controls.Tree;
import
 mx.controls.DataGrid;
import
 mx.controls.listClasses.ListBase;
import
 mx.events.DragEvent;
import
 mx.containers.Canvas;
import
 mx.managers.DragManager;
import
 mx.core.UIComponent;

[
Bindable
]


private
 var
 dataGridProvider:
ArrayCollection =
 new
 ArrayCollection()
;
/**

             * Handles the dragEnter event on the DataGrid control.

             * If the dragInitiator is the Tree control, then only nodes of type "restaurant"

             * are permitted to be dropped.

             * Here you can see that by examining the dragSource you can determine if

             * the control should accept the drop. The DataGrid control would not

             * know how to treat a branch+children from the Tree control, so only leaf (restaurant)

             * nodes are accepted.

             */


private
 function
 onDragEnter(
 event:
DragEvent )
 :
 void


{
            
if
(
 event.dragInitiator is
 Tree )
 {

var
 ds:
DragSource =
 event.dragSource;
if
(
 !
ds.hasFormat(
"treeItems"
)
 )
 return
;     // no useful data


                    var
 items:
Array =
 ds.dataForFormat(
"treeItems"
)
 as
 Array;
for
(
var
 i:
Number=
0; i <
 items.length; i++
)
 {


var
 item:
XML =
 XML(
items[
i])
;
if
(
 item.@type !=
 "restaurant"
 )
 return
; // not what we want


                    }

}
 
// If the Tree control passes or the dragInitiator is not a Tree control,

             // accept the drop.

            DragManager.acceptDragDrop(
UIComponent(
event.currentTarget))
; 
}
        
/**

             * Handles the dragOver event on the DataGrid control.

             * If the dragInitiator is the Tree control, only copy is allowed. Otherwise, a move

             * or link can take place from the List control.

             */


private
 function
 onDragOver(
 event:
DragEvent )
 :
 void


{

if
(
 event.dragInitiator is
 Tree )
 {


                    DragManager.showFeedback(
DragManager.COPY)
;
}
 else
 {


if
 (
event.ctrlKey)


                    DragManager.showFeedback(
DragManager.COPY)
;
else
 if
 (
event.shiftKey)



                        DragManager.showFeedback(
DragManager.LINK)
;
else
 {


                        DragManager.showFeedback(
DragManager.MOVE)
;
}


}

}
        
/**

             * Handles the dragExit event on the drop target and just hides the 

             * the drop feedback.

             */

private
 function
 onDragExit(
 event:
DragEvent )
 :
 void


{

var
 dropTarget:
ListBase=
ListBase(
event.currentTarget)
;   

            dropTarget.hideDropFeedback(
event)
;
}


/**

             * Handles the dragDrop event on the DataGrid when the 

             * drag proxy is released. 

             */

private
 function
 onGridDragDrop(
 event:
DragEvent )
 :
 void


{

var
 ds:
DragSource =
 event.dragSource;
var
 dropTarget:
DataGrid =
 DataGrid(
event.currentTarget)
;
var
 arr:
Array;
if
(
 ds.hasFormat(
"items"
)
 )
 {



                    arr =
 ds.dataForFormat(
"items"
)
 as
 Array;
}
 else
 if
(
 ds.hasFormat(
"treeItems"
)
 )
 {



                    arr =
 ds.dataForFormat(
"treeItems"
)
 as
 Array;
}

for
(
var
 i:
Number=
0; i <
 arr.length; i++
)
 {


var
 node:
XML =
 XML(
arr[
i])
;
var
 item:
Object =
 new
 Object()
;

                    item.label =
 node.@label;

                    item.type  =
 node.@type;

                    dataGridProvider.addItem(
item)
;
}



                onDragExit(
event)
; 
}

/**

            * Intercepts the dragComplete event on the Tree control

            * and prevents the default behavior from happening. This is necessary

            * if the item being dragged from the Tree control is dropped on a non-Tree

            * object, such as the DataGrid.

            */

private
 function
 onTreeDragComplete(
event:
DragEvent)
:
void
 {



                event.preventDefault()
;
}
        
/**

            * Selects all of the items in the List if Ctrl+A is picked when the List control

            * has focus.

            */

private
 function
 selectAllMaybe(
 event:
KeyboardEvent )
 :
 void


{

if
(
 event.ctrlKey &&
 event.keyCode ==
 65 )
 {


var
 l:
List =
 List(
event.currentTarget)
;
var
 allItems:
Array =
 new
 Array(
l.dataProvider.length)
;
for
(
var
 i:
Number=
0; i <
 allItems.length; i++
)
 {



                        allItems[
i]
 =
 i;
}


                    l.selectedIndices =
 allItems;
}


}

]]
>

</mx:Script>

<mx:XML
 id="treeData
" xmlns="">

<root>


            <node label="Massachusetts" type="state" data="MA">

                <node label="Boston" type="city" >

                    <node label="Smoke House Grill" type="restaurant" />

                    <node label="Equator" type="restaurant" />

                    <node label="Aquataine" type="restaurant" />

                    <node label="Grill 23" type="restaurant" />


                </node>

                <node label="Provincetown" type="city" >

                    <node label="Lobster Pot" type="restaurant" />

                    <node label="The Mews" type="restaurant" />

                </node>

            </node>


            <node label="California" type="state" data="CA">

                <node label="San Francisco" type="city" >

                    <node label="Frog Lane" type="restaurant" />

                </node>

            </node>

        </root>


</mx:XML>


<mx:Label
 x="34
" y="40
" text="Drag items from this Tree
"/>

<mx:Label
 x="34
" y="55
" text="(items are copied)
"/>


<mx:Tree
 x="34
" y="81
" width="181
" height="189
"

        dataProvider="{
treeData.node}
"

        labelField="@label
"

        dropEnabled="false
"

        dragEnabled="true
"

        dragComplete="onTreeDragComplete(
event)
"

        dragMoveEnabled="false
"
/>


<mx:Label
 x="291
" y="55
" text="Drop items from Tree here
"/>

<mx:DataGrid
 x="291
" y="81
" height="189
"

        dragEnabled="true
"

        dataProvider="{
dataGridProvider}
"

        dragEnter="onDragEnter(
event)
"

        dragOver="onDragOver(
event)
"

        dragDrop="onGridDragDrop(
event)
"

        dragExit="onDragExit(
event)
">


<mx:columns>

<mx:DataGridColumn
 headerText="Label
" dataField="label
"/>

<mx:DataGridColumn
 headerText="Type
" dataField="type
"/>


</mx:columns>

</mx:DataGrid>

</mx:Application>

Result

<script type="text/javascript"> // <![CDATA[ var props = new Object(); props.swf = "/devnet/flex/quickstart/working_with_tree/swfs/TreeDragFrom.swf"; props.id = "resultswfTreeDragFroms"; props.name = "resultswfTreeDragFrom"; props.w = "525"; props.h = "300"; props.ver = "9"; props.c = "#94AAB5"; props.wmode= "window"; props.menu="true"; var swfo = new SWFObject( props ); registerSWFObject( swfo, "swfTreeDragFrom" ); // ]]> </script>

To view the full source, right-click the Flex application and select View Source from the context menu.

Dragging and dropping to a Tree control

This following example shows how to drag and drop items to a Tree control from a List control. The Tree data provider is an XML object. Use the comments that precede the drag and drop event handler methods to guide you through the application.

Example

<?xml version="1.0" encoding="utf-8"?>

<mx:Application
 xmlns:mx="http://www.adobe.com/2006/mxml
" layout="absolute
">

<mx:Script>

<!
[
CDATA[


import
 mx.events.DragEvent;
import
 mx.managers.DragManager;
import
 mx.core.DragSource;
import
 mx.core.UIComponent;
import
 mx.controls.Tree;
/**

             * Called as soon as the dragProxy enters the target. You can add logic

             * to determine if the target will accept the drop based on the

             * dragInitiator, the data available in the dragSource.

             * Here the drop is blindly accepted.

             */


private
 function
 onDragEnter(
 event:
DragEvent )
 :
 void


{


                DragManager.acceptDragDrop(
UIComponent(
event.currentTarget))
;
}

/**

             * Called while the dragProxy is over the drop target. You can

             * use this function to determine the type of feedback to show.

             * Since the List is set to allow MOVE (the item is deleted

             * once dropped), different feedback possibilities are given.

             *

             * Also, for this application, the Tree control node the dragProxy is

             * over is selected. As the dragProxy moves, the Tree control's

             * selection changes.

             *

             * For a bit more complication, the drop is being allowed

             * only over nodes whose type is NOT 'state'.

             * The feedback is removed.

             */

private
 function
 onDragOver(
 event:
DragEvent )
 :
 void


{

var
 dropTarget:
Tree =
 Tree(
event.currentTarget)
;
var
 r:
int =
 dropTarget.calculateDropIndex(
event)
;

                tree.selectedIndex =
 r;
var
 node:
XML =
 tree.selectedItem as
 XML;
if
(
 node.@type ==
 "state"
 )
 {



                    DragManager.showFeedback(
DragManager.NONE)
;
return
;
}

if
 (
event.ctrlKey)



                    DragManager.showFeedback(
DragManager.COPY)
;
else
 if
 (
event.shiftKey)


                    DragManager.showFeedback(
DragManager.LINK)
;
else
 {



                    DragManager.showFeedback(
DragManager.MOVE)
;
}

}

/**

             * Called when the dragProxy is released

             * over the drop target. The information in the dragSource

             * is extracted and processed.

             *

             * The target node is determined and 

            * all of the data selected (the List has allowMultipleSection

            * set) is added.

             */

private
 function
 onDragDrop(
 event:
DragEvent )
 :
 void


{

var
 ds:
DragSource =
 event.dragSource;
var
 dropTarget:
Tree =
 Tree(
event.currentTarget)
;
var
 items:
Array =
 ds.dataForFormat(
"items"
)
 as
 Array;
var
 r:
int =
 tree.calculateDropIndex(
event)
;

                tree.selectedIndex =
 r;
var
 node:
XML =
 tree.selectedItem as
 XML;
var
 p:*
;
// if the selected node has children (it is type==city),


                // then add the items at the beginning

                if
(
 tree.dataDescriptor.hasChildren(
node)
 )
 {


                    p =
 node;

                    r =
 0;
}
 else
 {



                    p =
 node.parent()
;
}

for
(
var
 i:
Number=
0; i <
 items.length; i++
)
 {


var
 insert:
XML =
 <
node />
;

                    insert.@label =
 items[
i]
;

                    insert.@type  =
 "restaurant"
;

                    tree.dataDescriptor.addChildAt(
p, insert, r+
i)
;
}


}

/**

             * Called when the drag operation completes, whether 

             * successfully or not. The tree is cleared of its

             * selection.

             */

private
 function
 onDragComplete(
 event:
DragEvent )
 :
 void


{


                tree.selectedIndex =
 -
1;
}
        
]]
>

</mx:Script>
    
<mx:XML
 id="treeData
" xmlns="">


<root>

            <node label="Massachusetts" type="state" data="MA">

                <node label="Boston" type="city" >

                    <node label="Smoke House Grill" type="restaurant" />

                    <node label="Equator" type="restaurant" />

                    <node label="Aquataine" type="restaurant" />


                    <node label="Grill 23" type="restaurant" />

                </node>

                <node label="Provincetown" type="city" >

                    <node label="Lobster Pot" type="restaurant" />

                    <node label="The Mews" type="restaurant" />

                </node>


            </node>

            <node label="California" type="state" data="CA">

                <node label="San Francisco" type="city" >

                    <node label="Frog Lane" type="restaurant" />

                </node>

            </node>


        </root>

</mx:XML>
    
<mx:Array
 id="listData
">

<mx:String>
Johnny Rocket's
</mx:String>


<mx:String>
Jet Pizza
</mx:String>

<mx:String>
Steve's Greek
</mx:String>

<mx:String>
Sonsie
</mx:String>

<mx:String>
The Border Cafe
</mx:String>


</mx:Array>
    
<mx:Panel
 x="48
" y="125
" width="447
" height="351
" layout="absolute
" title="Drag onto Tree
">
    
<mx:Tree
 width="186
" left="10
" top="10
" bottom="10
" id="tree
"

            labelField="@label
"

            dataProvider="{
treeData.node}
"

            dropEnabled="false
"

            dragMoveEnabled="false
"

            dragEnter="onDragEnter(
event)
"

            dragOver="onDragOver(
event)
"

            dragDrop="onDragDrop(
event)
">


</mx:Tree>
        
<mx:List
 width="188
" height="206
" right="10
" bottom="10
" id="list
"

            allowMultipleSelection="true
"

            dataProvider="{
listData}
"

            dragEnabled="true
"

            dragMoveEnabled="true
"

            dragComplete="onDragComplete(
event)
">


</mx:List>
        
<mx:Text
 x="229
" y="10
" text="Drag from the list below to the tree
" width="188
" height="39
"/>


<mx:Label
 x="229
" y="69
" text="restaurants
"/>

</mx:Panel>
    
</mx:Application>

Result

<script type="text/javascript"> // <![CDATA[ var props = new Object(); props.swf = "/devnet/flex/quickstart/working_with_tree/swfs/TreeDragTo.swf"; props.id = "resultswfTreeDragTo"; props.name = "resultswfTreeDragTo"; props.w = "525"; props.h = "600"; props.ver = "9"; props.c = "#94AAB5"; props.wmode= "window"; props.menu="true"; var swfo = new SWFObject( props ); registerSWFObject( swfo, "swfTreeDragTo" ); // ]]> </script>

To view the full source, right-click the Flex application and select View Source from the context menu.