MX Tree control

The 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 controls

A 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:

Tree control

Creating a Tree control

You 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:

Collections
A collection implementation, such as an XMLListCollection or ArrayCollection object. The DefaultDataDescriptor class includes code to handle collections efficiently. Always use a collection as the data provider if the data in the menu changes dynamically; otherwise the Tree control might display obsolete data.

XML
A string containing valid XML text, or any of the following objects containing valid E4X format XML data: <fx:XML> or <fx:XMLList> compile-time tag, or an XML or XMLList object.

Other objects
An array of items, or an object that contains an array of items, where a node’s children are contained in an item named children.

The DefaultDataDescriptor class also supports using an <fx:Model> tag as a data provider for a menu, but all leaf nodes must have the name children; As a general rule, it is a better programming practice to use the <fx:XML> or <fx:XMLList> tags when you need a Tree data provider that uses binding.

For more information on hierarchical objects and data descriptors, including a detailed description of the formats supported by the DefaultDataDescriptor, see Data descriptors and hierarchical data structure.

The following code contains a single Tree control that defines the tree shown in the image in MX Tree control. This uses an XMLListCollection wrapper around an <fx:XMLList> tag. By using an XMLListCollection, you can modify the underlying XML data provider by changing the contents of the MailBox XMLListCollection, and the Tree control will represent the changes to the data. This example also does not use the <mx:dataProvider> tag because dataProvider is the default property of the Tree control.

<?xml version="1.0"?>
<!-- dpcontrols/TreeSimple.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">
    
   <mx:Tree id="tree1" 
       labelField="@label" 
       showRoot="true" 
       width="160">
      <mx:XMLListCollection id="MailBox">
         <fx:XMLList>
            <folder label="Mail">
               <folder label="INBOX"/>
               <folder label="Personal Folder">
                  <Pfolder label="Business" />
                  <Pfolder label="Demo" /> 
                     <Pfolder label="Personal" isBranch="true" /> 
                     <Pfolder label="Saved Mail" /> 
                  </folder>
                  <folder label="Sent" />
                  <folder label="Trash" />
            </folder>
         </fx:XMLList>
      </mx:XMLListCollection>
   </mx:Tree>
</s:Application>

The executing SWF file for the previous example is shown below:

The tags that represent tree nodes in the XML data can have any name. The Tree control reads the XML and builds the display hierarchy based on the nested relationship of the nodes. For information on valid XML structure, see Hierarchical data objects.

Some data providers have a single top level node, called a root node. Other data providers are lists of nodes and do not have a root node. In some cases, you might not want to display the root node as the Tree root. To prevent the tree from displaying the root node, specify the showRoot property to false; doing this does not affect the data provider contents, only the Tree display. You can only specify a falseshowRoot property for data providers that have roots, that is, XML and Object-based data providers.

A branch node can contain multiple child nodes, and, by default, appears as a folder icon with a disclosure triangle that lets users open and close the folder. Leaf nodes appear by default as file icons and cannot contain child nodes.

When a Tree control displays a node of a non-XML data provider, by default, it displays the value of the label property of the node as the text label. When you use an E4X XML-based data provider, however, you must specify the label field, even if the label is identified by an attribute named “label”. To specify the label field, use the labelField property; for example, if the label field is the label attribute, specify labelField="@label".

Handling Tree control events

You 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>

    <s:Form>
        <s:FormItem label="Change Event:">
            <s:Label id="forChange" width="150"/>          
        </s:FormItem>
        <s:FormItem label="Open Event:">
            <s:Label id="forOpen" width="150"/>            
        </s:FormItem>
    </s:Form>
</s:Application>

The executing SWF file for the previous example is shown below:

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 node

By 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>

The executing SWF file for the previous example is shown below:

Specifying Tree control icons

The Tree control provides four techniques for specifying node icons:

  • The folderOpenIcon, folderClosedIcon, and defaultLeafIcon properties

  • Data provider node icon fields

  • The setItemItcon() method

  • The iconFunction property

Using icon properties

You 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 fields

You 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>

The executing SWF file for the previous example is shown below:

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() method

You 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>

The executing SWF file for the previous example is shown below:

Using an icon function

You 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 node

By 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>

The executing SWF file for the previous example is shown below:

Adding and removing 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 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>

The executing SWF file for the previous example is shown below:

Tree user interaction

You can let users edit tree control labels. The controls also support several keyboard navigation and editing keys.

Editing a node label at run time

Set 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:

Event

Description

itemEditBegin

Dispatched when the editedItemPosition property has been set and the cell can be edited.

itemEditEnd

Dispatched when cell editing session ends for any reason.

itemFocusIn

Dispatched when tree node gets the focus: when a user selects the label or tabs to it.

itemFocusOut

Dispatched when a label loses focus.

itemClick

Dispatched when a user clicks on an item in the control.

These events are commonly used in custom item editors. For more information see MX item renderers and item editors.

Using the keyboard to edit labels

If you set the Tree editable property to true, you can use the following keys to edit labels:

Key

Description

Down Arrow

Page Down

End

Moves the caret to the end of the label.

Up Arrow

Page Up

Home

Moves the caret to the beginning of the label.

Right Arrow

Moves the caret forward one character.

Left Arrow

Moves the caret backward one character.

Enter

Ends editing and moves selection to the next visible node, which can then be edited. At the last node, selects the label.

Shift Enter

Ends editing and moves selection to the previous visible node, which can then be edited. At the first node, selects the label.

Escape

Cancels the edit, restores the text, and changes the row state from editing to selected.

TAB

When in editing mode, accepts the current changes, selects the row below, and goes into editing mode with the label text selected. If at the last element in the tree or not in editing mode, sends focus to the next control.

Shift-TAB

When in editing mode, accepts the current changes, selects the row above, and goes into editing mode. If at the first element in the tree or not in editing mode, sends focus to the previous control.

Tree Navigation keys

When a Tree control is not editable and has focus from clicking or tabbing, you use the following keys to control it:

Key

Description

Down Arrow

Moves the selection down one. When the Tree control gets focus, use the Down arrow to move focus to the first node.

Up Arrow

Moves the selection up one item.

Right Arrow

Opens a selected branch node. If a branch is already open, moves to the first child node.

Left Arrow

Closes a selected branch node. If a leaf node or a closed branch node is currently selected, selects the parent node.

Spacebar or * (Asterisk on numeric keypad)

Opens or closes a selected branch node (toggles the state).

+ (Plus sign on numeric keypad)

Opens a selected branch node.

- (Minus sign on numeric keypad)

Closes a selected branch node.

Control + Arrow keys

Moves the focus, but does not select a node. Use the Spacebar to select a node.

End

Moves the selection to the bottom of the list.

Home

Moves the selection to the top of the list.

Page down

Moves the selection down one page.

Page up

Moves the selection up one page.

Control

If the allowMultipleSelection property is true, allows multiple noncontiguous selections.

Shift

If the allowMultipleSelection property is true, allows multiple contiguous selections.