MX Tree controlThe Tree control lets a user view hierarchical data arranged as an expandable tree. For complete reference information, see the ActionScript 3.0 Reference for the Adobe Flash Platform. For information on hierarchical data providers, see Hierarchical data objects. For information on the following topics, which are often important for using advanced Tree controls, see:
About Tree controlsA Tree control is a hierarchical structure of branch and leafnodes. 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. By default, a leaf is represented by a text label beside a file icon and a branch is represented by a text label beside a folder icon with a disclosure triangle that a user can open to expose children. The following image shows a Tree control: ![]() Creating a Tree controlYou define a Tree control in MXML by using the <mx:Tree> tag. The Tree class extends the List class and Tree controls take all of the properties and methods of the List control. For more information about using the List control, see MX List control. Specify an id value if you intend to refer to a control elsewhere in your MXML, either in another tag or in an ActionScript block. The Tree control normally gets its data from a hierarchical data provider, such as an XML structure. If the Tree represents dynamically changing data, you should use a list or collection, such as the standard ArrayCollection, or XMLListCollection object, as the data provider. The Tree control uses a data descriptor to parse and manipulate the data provider content. By default, the Tree control uses a DefaultDataDescriptor instance, but you can create your own class and specify it in the Tree control’s dataDescriptor property. The DefaultDataDescriptor class supports the following types of data:
Handling Tree control eventsYou typically use events to respond to user interaction with a Tree control. Since the Tree control is derived from the List control, you can use all of the events defined for the List control. The Tree control also dispatches several Event and TreeEventclass events, including Event.change and TreeEvent.itemOpen. The following example defines event handlers for the change and itemOpen events: <?xml version="1.0"?>
<!-- dpcontrols/TreeEvents.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import flash.events.*;
import mx.events.*;
import mx.controls.*;
private function changeEvt(event:Event):void {
var theData:String = ""
if (event.currentTarget.selectedItem.@data) {
theData = " Data: " + event.currentTarget.selectedItem.@data;
}
forChange.text = event.currentTarget.selectedItem.@label + theData;
}
private function itemOpenEvt(event:TreeEvent):void {
forOpen.text = event.item.@label;
}
]]>
</fx:Script>
<mx:Tree id="XMLTree1" width="150" height="170"
labelField="@label" itemOpen="itemOpenEvt(event);"
change="changeEvt(event);">
<mx:XMLListCollection id="MailBox">
<fx: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="Personal" data="0" isBranch="true" />
<node label="Saved Mail" data="5" />
</node>
<node label="Sent" data="15"/>
<node label="Trash" data="5"/>
</node>
</fx:XMLList>
</mx:XMLListCollection>
</mx:Tree>
<mx:Form>
<mx:FormItem label="Change Event:">
<mx:Label id="forChange" width="150"/>
</mx:FormItem>
<mx:FormItem label="Open Event:">
<mx:Label id="forOpen" width="150"/>
</mx:FormItem>
</mx:Form>
</s:Application>
In this example, you define event listeners for the change and itemOpen events. The Tree control broadcasts the change event when the user selects a tree item, and broadcasts the itemOpen event when a user opens a branch node. For each event, the event handler displays the label and the data property, if any, in a TextArea control. Expanding a tree nodeBy default, the Tree control displays the root node or nodes of the tree when it first opens. If you want to expand a node of the tree when the tree opens, you can use the expandItem() method of the Tree control. The following change to the example in Handling Tree control events calls the expandItem() method as part of the Tree control’s creationComplete event listener to expand the root node of the tree: <fx:Script>
<![CDATA[
.
.
.
private function initTree():void {
XMLTree1.expandItem(MailBox.getItemAt(0), true);
forOpen.text=XMLTree1.openItems[0].@label;
}
]]>
</fx:Script>
<mx:Tree id="tree1" ... creationComplete="initTree();" >
...
</mx:Tree>
This example must use the Tree control’s creationComplete event, not the initialize event, because the data provider is not fully initialized and available until the creationComplete event. The Tree control openItems property is an Array containing all expanded tree nodes. The following line in the example code displays the label of the first (and only) open item in the tree: forOpen.text=XMLTree1.openItems[0].@label; In this example, however, you could also get the openItems box to indicate the initial open item by setting the expandItem() method to dispatch an itemOpen event. You can do this by specifying the fourth, optional parameter of the expandItem() method to true. The true fourth parameter causes the tree to dispatch an open event when the item opens. The following example shows the use of the fourth parameter: XMLTree1.expandItem(MailBox.getItemAt(0), true, false, true); You can programmatically walk down a Tree control’s nodes and open a node without knowing what depth you are at in the Tree’s data provider. One way to do this is by using the children() method of a node’s XML object to test whether the node has any children; you can use the expandItem() method to open the node. The following example opens nodes in the Tree control based on the values of query string parameters. For example, if you pass app_url?0=1&1=2&2=0 to the application, then Flex opens the second item at the top level of the tree, the third item at the next level, and the first item at the third level of the tree (nodes are zero-based). <?xml version="1.0"?>
<!-- dpcontrols/DrillIntoTree.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="initApp();">
<fx:Script>
<![CDATA[
import mx.collections.XMLListCollection;
import mx.core.FlexGlobals;
[Bindable]
private var treeData:XML =
<root>
<node label="Monkeys">
<node label="South America">
<node label="Coastal"/>
<node label="Inland"/>
</node>
<node label="Africa" isBranch="true"/>
<node label="Asia" isBranch="true"/>
</node>
<node label="Sharks">
<node label="South America" isBranch="true"/>
<node label="Africa" isBranch="true"/>
<node label="Asia" >
<node label="Coastal"/>
<node label="Inland"/>
</node>
</node>
</root>;
private var openSequence:Array = [];
private function initApp():void {
/* Parse URL and place values into openSequence Array.
This lets you pass any integers on the query string,
in any order. So:
http://localhost/flex/flex2/DrillIntoTree.swf?0=1&1=2&2=0
results in an array of selections like this:
0:1
1:2
2:0
Non-ints are ignored.
The Array is then used to drill down into the tree.
*/
var paramLength:int = 0;
for (var s:String in FlexGlobals.topLevelApplication.parameters) {
if (!isNaN(Number(s))) {
openSequence[s] = FlexGlobals.topLevelApplication.parameters[s];
paramLength += 1;
}
}
openTree();
}
private function openTree():void {
var nodeList:XMLListCollection =
myTree.dataProvider as XMLListCollection;
var node:XMLList = nodeList.source;
for(var i:int=0; i < openSequence.length; i++) {
var j:int = openSequence[i];
var n:XML = node[j];
if( n.children() != null ) {
myTree.expandItem(n,true,false);
node = n.children();
} else {
break;
}
}
if( n != null ) myTree.selectedItem = n;
}
]]>
</fx:Script>
<mx:Tree id="myTree"
y="50"
width="221"
height="257"
horizontalCenter="0"
dataProvider="{treeData.node}"
labelField="@label"/>
</s:Application>
Specifying Tree control iconsThe Tree control provides four techniques for specifying node icons:
Using icon propertiesYou can use the folderOpenIcon, folderClosedIcon, and defaultLeafIcon properties to control the Tree control icons. For example, the following code specifies a default leaf icon, and icons for the open and closed states of branch nodes: <mx:Tree folderOpenIcon="@Embed(source='open.jpg')"
folderClosedIcon="@Embed(source='closed.jpg')"
defaultLeafIcon="@Embed(source='def.jpg')">
Using icon fieldsYou can specify an icon displayed with each Tree leaf when you populate it by using XML, as the following example shows: <?xml version="1.0"?>
<!-- dpcontrols/TreeIconField.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
[Bindable]
[Embed(source="assets/radioIcon.jpg")]
public var iconSymbol1:Class;
[Bindable]
[Embed(source="assets/topIcon.jpg")]
public var iconSymbol2:Class;
]]>
</fx:Script>
<mx:Tree iconField="@icon"
labelField="@label"
showRoot="false"
width="160">
<fx:XMLList>
<node label="New">
<node label="HTML Document" icon="iconSymbol2"/>
<node label="Text Document" icon="iconSymbol2"/>
</node>
<node label="Close" icon="iconSymbol1"/>
</fx:XMLList>
</mx:Tree>
</s:Application>
In this example, you use the iconField property to specify the field of each item containing the icon. You use the Embed metadata to import the icons, then reference them in the XML definition. You cannot use icon fields to specify icons for individual branch nodes; instead you must use the Tree control’s folderOpenIcon, folderClosedIcon properties, each of which specifies an icon to use for all open or closed branches, or use the setItemIcon() method to set individual node icons. Using the setItemIcon() methodYou can use the setItemIcon() method to specify the icon, or both the open and closed icons for a tree item. This method lets you dynamically specify and change icons for individual branches and nodes. For details on this function see setItemIcon() in ActionScript 3.0 Reference for the Adobe Flash Platform. The following example sets the open and closed node icon for the first branch node and the icon for the second branch (which does not have any leaves): <?xml version="1.0"?>
<!-- dpcontrols/TreeItemIcon.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
[Bindable]
[Embed(source="assets/radioIcon.jpg")]
public var iconSymbol1:Class;
[Bindable]
[Embed(source="assets/topIcon.jpg")]
public var iconSymbol2:Class;
private function setIcons():void {
myTree.setItemIcon(myTree.dataProvider.getItemAt(0),
iconSymbol1, iconSymbol2);
myTree.setItemIcon(myTree.dataProvider.getItemAt(1),
iconSymbol2, null);
}
]]>
</fx:Script>
<mx:Tree id="myTree" labelField="@label"
showRoot="false"
width="160" initialize="setIcons();">
<fx:XMLList>
<node label="New">
<node label="HTML Document"/>
<node label="Text Document"/>
</node>
<node label="Close"/>
</fx:XMLList>
</mx:Tree>
</s:Application>
Using an icon functionYou can use the Tree control iconFunction property to specify a function that dynamically sets all icons for the tree. For information on using the iconFunction property in Flex controls, see Specifying an icon to the List control. Opening a Tree control to a specific nodeBy default, a Tree control is collapsed when it initializes, but you can initialize it so that it is expanded with a specific node selected, as the following example shows. 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. <?xml version="1.0"?>
<!-- dpcontrols/TreeOpenNode.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import flash.events.*;
import mx.events.*;
import mx.controls.*;
private function initTree():void {
XMLTree1.expandItem(MailBox.getItemAt(0), true);
XMLTree1.selectedIndex = 2;
}
]]>
</fx:Script>
<mx:Tree id="XMLTree1"
width="150" height="170"
labelField="@label"
creationComplete="initTree();">
<mx:XMLListCollection id="MailBox">
<fx: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>
</fx:XMLList>
</mx:XMLListCollection>
</mx:Tree>
</s:Application>
Adding and removing leaf nodes at run timeYou 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 dynamically add leaf (employee) nodes to the Operations department branch at run time. You can also remove any leaf node, including any you create 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. <?xml version="1.0"?>
<!-- dpcontrols/TreeAddRemoveNode.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx: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];
}
}
}
]]>
</fx:Script>
<mx:Tree id="tree"
top="72" left="50"
dataProvider="{companyData}"
labelFunction="treeLabel"
height="225" width="300"/>
<mx:VBox>
<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:VBox>
</s:Application>
Tree user interactionYou can let users edit tree control labels. The controls also support several keyboard navigation and editing keys. Editing a node label at run timeSet the editable property of the Tree control to true to make node labels editable at run time. To edit a node label, the user selects the label, and then enters a new label or edits the existing label text. To support label editing, the Tree control’s List superclass uses the following events. These events belong to the ListEvent class:
These events are commonly used in custom item editors. For more information see MX item renderers and item editors. Using the keyboard to edit labelsIf you set the Tree editable property to true, you can use the following keys to edit labels:
Tree Navigation keysWhen a Tree control is not editable and has focus from clicking or tabbing, you use the following keys to control it:
|
|