Flex Quick Start: Building an advanced user interface
Table of contents
- Using the Repeater component
- Skinning your components
- Using item renderers
- Creating item editors
- Using data providers
- Adding drag-and-drop support
- Working with Tree controls
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.