The TreeView is an extended ListView control with the ability to accept and display hierachical or complex data. The TreeView is equipped with an internal lineariser, which maps the given complex data structure to a linear list by maintaining expanded and collapsed items. The TreeView can be customised in several ways. This document describes all the possibilities to configure a TreeView control.
- TreeView demo application
- Setting up the data
- Creating a label function
- Using the public interface
- Styling the TreeView
- Setting custom icons
TreeView demo application
Click the image below for an interactive interface demo of the TreeView control. The demo lets you change data, styles and functionality of the component and provides information about the events dispatched and styles set.
If the popup does not do well, click here.
Setting up the data
The TreeView can work with any kind of hierachical data. There is no restriction in the type of the particular data source items. Its also possible to mix different types in one data source. The TreeView only needs to know for each item, how many children it contains (numItems) and how to get a child item at a specific index (getItemAt). There are three ways to provide this information.
- Using a supported data source type without any preperation.
- Implementing the IDataProvider interface in all item classes, if you are free in designing the data source.
- Setting up a data source adapter, if you want to use a given (closed) object as the data source of the tree.
Using a supported data source
The TreeView already comes with support for a few types, from that it automatically extracts the numItems and getItemAt information.
- XML
- Array
- All maps, sets and the ArrayList from the AS3Commons Collections package
Look at the DataSourceAdapterFactory.as and see, how the TreeView handles the different item types. The example below shows an XML acting as the data source of the TreeView.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; public class TreeViewExample extends Example { public function TreeViewExample() { var treeView : TreeView = new TreeView(); treeView.dataSource = new XML( <item name="Root"> <item name="Node_1"> <item name="Node_1_1" /> <item name="Node_1_2" /> </item> <item name="Node_2" /> </item> ); addChild(treeView); } } } |
Implementing the data provider interface
The two requirements above are modelled in a very small IDataProvider interface.
1 2 3 4 5 6 | package com.sibirjak.asdpc.core.dataprovider { public interface IDataProvider { function getItemAt(index : uint) : *; function get numItems() : uint; } } |
Building a complex data source by implementing this interface is easy and may look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; public class SimpleDataProviderExample extends Example { public function SimpleDataProviderExample() { var dataSource : Node = new Node("Root"); var node_1 : Node = new Node("Node_1"); node_1.addNode(new Node("Node_1_1")); node_1.addNode(new Node("Node_1_2")); dataSource.addNode(node_1); dataSource.addNode(new Node("Node_2")); var treeView : TreeView = new TreeView(); treeView.dataSource = dataSource; addChild(treeView); } } } import com.sibirjak.asdpc.core.dataprovider.IDataProvider; internal class Node implements IDataProvider { private var _name : String; private var _childNodes : Array = new Array(); public function Node(name : String) { _name = name; } public function addNode(node : Node) : void { _childNodes.push(node); } public function getItemAt(index : uint) : * { return _childNodes[index]; } public function get numItems() : uint { return _childNodes.length; } public function get name() : String { return _name; } } |
The Flash plugin is required to view this object.
Creating a data source adapter
If the data source cannot implement the IDataProvider interace, it is necessary to write a custom data source adapter by implementing the IDataSourceAdapter interface. The adapter accepts an item from the original data source and knows how to retrieve numItems and getItemAt information from this item.
1 2 3 4 5 6 | package com.sibirjak.asdpc.core.dataprovider { public interface IDataSourceAdapter extends IDataProvider { function get dataSource() : *; function cleanUp() : void; } } |
Creating a custom data source adapter is a two-step procedure.
- Creating the adapter
- Creating a data source adapter factory function
Creating an adapter is as simple as it could be. Let’s assume our data source item is a Node object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | internal class Node { private var _name : String; private var _childNodes : Array = new Array(); public function Node(name : String) { _name = name; } public function addNode(node : Node) : void { _childNodes.push(node); } public function get name() : String { return _name; } public function get childNodes() : Array { return _childNodes; } } |
The data source adapter then will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import com.sibirjak.asdpc.core.dataprovider.IDataSourceAdapter; internal class NodeAdapter implements IDataSourceAdapter { private var _node : Node; public function NodeAdapter(node : Node) { _node = node; } public function get dataSource() : * { return _node; } public function getItemAt(index : uint) : * { return _node.childNodes[index]; } public function get numItems() : uint { return _node.childNodes.length; } public function cleanUp() : void { // empty } } |
We now have to tell the TreeView that it should use the NodeAdapter if it encounters a Node object. To do so, we write da data source adapter function.
1 2 3 4 | private function getAdapter(item : *) : IDataProvider { if (item is Node) return new NodeAdapter(item); return null; } |
Finally, we assign this function to the appropriate TreeView property.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; import com.sibirjak.asdpc.core.dataprovider.IDataProvider; public class SimpleDataSourceAdapterExample extends Example { public function SimpleDataSourceAdapterExample() { var dataSource : Node = new Node("Root"); var node_1 : Node = new Node("Node_1"); node_1.addNode(new Node("Node_1_1")); node_1.addNode(new Node("Node_1_2")); dataSource.addNode(node_1); dataSource.addNode(new Node("Node_2")); var treeView : TreeView = new TreeView(); treeView.dataSource = dataSource; treeView.dataSourceAdapterFunction = getAdapter; addChild(treeView); } private function getAdapter(item : *) : IDataProvider { if (item is Node) return new NodeAdapter(item); return null; } } } |
The Flash plugin is required to view this object.
Example: Visualising the display List
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; import com.sibirjak.asdpc.core.dataprovider.IDataProvider; import flash.display.DisplayObjectContainer; public class DisplayListExample extends Example { public function DisplayListExample() { var treeView : TreeView = new TreeView(); treeView.setSize(440, 360); treeView.dataSource = stage; treeView.dataSourceAdapterFunction = getAdapter; treeView.expandNodeAt(0); addChild(treeView); } private function getAdapter(item : *) : IDataProvider { if (item is DisplayObjectContainer) { return new DOCAdapter(item); } return null; } } } import com.sibirjak.asdpc.core.dataprovider.IDataSourceAdapter; import flash.display.DisplayObjectContainer; internal class DOCAdapter implements IDataSourceAdapter { private var _view : DisplayObjectContainer; public function DOCAdapter(view : DisplayObjectContainer) { _view = view; } public function get dataSource() : * { return _view; } public function getItemAt(index : uint) : * { return _view.getChildAt(index); } public function get numItems() : uint { return _view.numChildren; } public function cleanUp() : void { // empty } } |
The Flash plugin is required to view this object.
Creating a label function
By default, the TreeView looks for the public properties “label” or “name” to extract a suitable label text, that can be displayed. Look at the genericToStringFunction.as and see, how the TreeView extracts the label text from an item. However, in some cases such a property does not exist or a different than the default value should be displayed. Here it is possible to assign a label function, wich is used instead of the generic toString function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; import com.sibirjak.asdpc.listview.renderer.ListItemContent; public class LabelFunctionExample extends Example { public function LabelFunctionExample() { var dataSource : Node = new Node("Root"); var node_1 : Node = new Node("Node_1"); node_1.addNode(new Node("Node_1_1")); node_1.addNode(new Node("Node_1_2")); dataSource.addNode(node_1); dataSource.addNode(new Node("Node_2")); var treeView : TreeView = new TreeView(); treeView.dataSource = dataSource; treeView.setStyle( ListItemContent.style.labelFunction, labelFunction ); addChild(treeView); } private function labelFunction(data : TreeNodeData) : String { return Node(data.item).key; } } } import com.sibirjak.asdpc.core.dataprovider.IDataProvider; internal class Node implements IDataProvider{ public var key : String; public var childNodes : Array = new Array(); public function Node(theKey : String) { key = theKey; } public function addNode(node : Node) : void { childNodes.push(node); } public function getItemAt(index : uint) : *{ return childNodes[index]; } public function get numItems() : uint{ return childNodes.length; } } |
The label function is set as a style with the constant “ListItemContent.style.labelFunction” used as the property name. All styles properties within the ASDPC have their particular constant. This can be useful, if your IDE supports property completion. The style “labelFunction” is defined for the class ListItemContent, which finally renders the item. More to this in the Styling the TreeView section.
The Flash plugin is required to view this object.
Using the public interface
The public interface of the TreeView control extends the ListView control interface and offers expand/collapse, selection and scroll operations beside the possibilities to set and retrieve data information. Visit the APIdoc for a comprehensive description of the TreeView interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; public class PublicInterfaceExample extends Example { public function PublicInterfaceExample() { // Fully expanded var treeView : TreeView = new TreeView(); treeView.setSize(200, 200); treeView.dataSource = getDataSource(); treeView.expandNodeAt(0, true); addChild(treeView); // Expanded and selected treeView = new TreeView(); treeView.setSize(200, 80); treeView.dataSource = getDataSource(); treeView.expandNodeAt(0); treeView.selectItemAt(1); treeView.moveTo(210, 0); addChild(treeView); // Fully expanded and scrolled treeView = new TreeView(); treeView.setSize(200, 100); treeView.dataSource = getDataSource(); treeView.expandNodeAt(0, true); treeView.scrollToItemAt(2); treeView.moveTo(210, 100); addChild(treeView); } private function getDataSource() : * { return new XML( <item name="Root"> <item name="Node_1"> <item name="Node_1_1" /> <item name="Node_1_2"> <item name="Node_1_2_1" /> <item name="Node_1_2_2" /> </item> <item name="Node_1_3" /> </item> <item name="Node_2"> <item name="Node_2_1" /> <item name="Node_2_2" /> </item> </item> ); } } } |
The Flash plugin is required to view this object.
Styling the TreeView
The TreeView (as with all ASDPC controls) can be extensively customised in functionality and appearance using styles. Most of the styles can be set or reset at runtime, wich means after the control has been added to the stage the first time.
Style management
The ASDPC style management is pretty easy to understand. Styles can be set directly to an object that defines that styles:
treeView.setSyle(TreeView.style.showRoot, false); scrollBar.setSyle(ScrollBar.style.scrollButtonVisibility, false);
In composite structures (such as all controls are) you may set a sub component style also to the containing object:
treeView.setSyle(ScrollBar.style.scrollButtonVisibility, false); treeView.setSyle(TreeNodeRenderer.style.indent, 24);
To work with styles it is then necessary to know:
- The structure of the control that should by styled
- The style properties of the control and of all its sub components
TreeView structure
The structure of a TreeView and all available styles are visualised in the info window of the demo application. Here again a short list of all sub components of a TreeView. Please refer to the APIDoc and to the source code to get informed about the particular styling possibilies of each sub component.
TreeView
TreeNodeRenderer
ListItemBackgroundSkin
ConnectorContainer
Connector
DottedConnectorSkin
ListItemContent
Label
DirectoryIcon
DisclosureButton
ScrollBar
Button
ButtonSkin
ScrollButtonIconSkin
Track
ScrollTrackSkin
Thumb
ButtonSkin
ScrollThumbIconSkinStyle property definitions
Styles in the ASDPC library are always defined in the class, that use the style. The label font size is defined by the Label class. The button background color is defined by the ButtonSkin class, and the color of the scroll button triangle icon is defined by the ScrollButtonIconSkin class.
By convention, a style property name includes the name of the class, which it defines. E.g. the style property name for the visibility of the root node of the TreeView is “treeView_showRoot”. You do not know those constant values, since all style properties are available as ActionScript constants. The constant for the root node visibility is simply TreeView.style.showRoot. Style property constants are either defined in a separate style object or directly to the class, that use the style. Examples:
- Label color: Label.style.color
- List item height: ListView.style.itemSize
- Scroll button visibility: ScrollBar.style.scrollButtonVisibility
- Button background colors: ButtonSkin.style_backgroundColors
- Tooltip border color: ToolTipSkin.style_borderColor
Styling example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; import com.sibirjak.asdpc.core.constants.Visibility; import com.sibirjak.asdpc.scrollbar.ScrollBar; import com.sibirjak.asdpc.treeview.renderer.ConnectorContainer; import com.sibirjak.asdpc.treeview.renderer.DisclosureButton; import com.sibirjak.asdpc.treeview.renderer.TreeNodeRenderer; import com.sibirjak.asdpc.treeview.renderer.skins.DisclosureButtonBoxIconSkin; /** * @author jes 04.02.2010 */ public class StylingExample extends Example { public function StylingExample() { var treeView : TreeView = new TreeView(); treeView.setSize(260, 125); treeView.expandNodeAt(0); treeView.dataSource = new XML( <item name="Root"> <item name="Node_1"> <item name="Node_1_1" /> <item name="Node_1_2" /> <item name="Node_1_3" /> </item> <item name="Node_2" /> </item> ); treeView.setStyles([ // item size TreeView.style.itemSize, 25, // scrollbar and button visibility TreeView.style.scrollBarVisibility, Visibility.VISIBLE, ScrollBar.style.scrollButtonVisibility, Visibility.VISIBLE, // box shaped disclosure button DisclosureButton.style_expandedIconSkin, DisclosureButtonBoxIconSkin, DisclosureButton.style_collapsedIconSkin, DisclosureButtonBoxIconSkin, DisclosureButton.style_size, 9, DisclosureButtonBoxIconSkin.style_fillColor, 0x000000, DisclosureButtonBoxIconSkin.style_iconColor, 0xFFFFFF, // connectors below button and icon ConnectorContainer.style_connectorAtButton, true, ConnectorContainer.style_connectorAtIcon, true, // list item indent TreeNodeRenderer.style.indent, 26, // list item colors TreeNodeRenderer.style.separator, true, TreeNodeRenderer.style.separatorColor, 0xDDDDDD, TreeNodeRenderer.style.evenIndexBackgroundColors, [0xFFFFFF, 0xEEEEEE], TreeNodeRenderer.style.oddIndexBackgroundColors, [0xFFFFFF, 0xEEEEEE], TreeNodeRenderer.style.overBackgroundColors, [0xDDDDDD, 0xBBBBBB], TreeNodeRenderer.style.selectedBackgroundColors, [0x666666, 0x999999], ]); addChild(treeView); } } } |
The Flash plugin is required to view this object.
Setting custom icons
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | package com.sibirjak.asdpc.treeview { import com.sibirjak.asdpc.common.Example; import com.sibirjak.asdpc.treeview.renderer.DirectoryIcon; import com.sibirjak.asdpc.treeview.renderer.DisclosureButton; public class DynamicIconExample extends Example { [Embed(source="assets/plus.png")] private var _plus : Class; [Embed(source="assets/minus.png")] private var _minus : Class; [Embed(source="assets/document.png")] private var _document : Class; [Embed(source="assets/folder.png")] private var _folder : Class; [Embed(source="assets/folder_open.png")] private var _folder_open : Class; [Embed(source="assets/as.png")] private var _as : Class; [Embed(source="assets/html.png")] private var _html : Class; [Embed(source="assets/php.png")] private var _php : Class; [Embed(source="assets/pdf.png")] private var _pdf : Class; [Embed(source="assets/htdocs.png")] private var _htdocs : Class; [Embed(source="assets/htdocs_open.png")] private var _htdocs_open : Class; public function DynamicIconExample() { var treeView : TreeView = new TreeView(); treeView.dataSource = new XML( <folder name="C:\\"> <folder name="documents"> <file name="serialz.txt" /> <file name="asdoc.pdf" /> <file name="images.zip" /> </folder> <folder name="htdocs"> <file name="index.php" /> <file name="contact.html" /> <file name="popup.html" /> </folder> <file name="manual.pdf" /> <file name="asdpc.exe" /> </folder> ); // disclosure buttons treeView.setStyles([ DisclosureButton.style_collapsedIconSkin, _plus, DisclosureButton.style_expandedIconSkin, _minus, DisclosureButton.style_size, 16]); // default icons treeView.setStyles([ DirectoryIcon.style.branchClosedIconSkin, _folder, DirectoryIcon.style.branchOpenIconSkin, _folder_open, DirectoryIcon.style.leafIconSkin, _document ]); // dynamic icons var iconFunction : Function = function (data : TreeNodeData) : Class { var label : String = XML(data.item).attribute("name"); if (label.indexOf(".as") > -1) return _as; else if (label.indexOf(".php") > -1) return _php; else if (label.indexOf(".pdf") > -1) return _pdf; else if (label.indexOf(".html") > -1) return _html; else if (label.indexOf("htdocs") > -1) { if (data.isExpanded) return _htdocs_open; return _htdocs; } return null; // else use default icon }; treeView.setStyle(DirectoryIcon.style.iconSkinFunction, iconFunction); treeView.expandNodeAt(0); addChild(treeView); } } } |
The Flash plugin is required to view this object.

Great work! I have a problem though.
Could you tell me how can I style a specific item in the list? Something like myTreeView.getItemAt(4).setStyle(…) would be nice :) I want to change icons of the items depending on their data.
I have added an example how to change the icon of a tree item. You have here 3 possibilities:
1. Change one or all of the default icons (branch, branch open, leaf)
2. Set custom icon depending on the current data
3. Set custom icon depending on the data and on the state of the node (open, closed, leaf)
Setting custom icons
Thank you! These components are great – clean and easy to use (after you know how).
hello, at thanks for your great work. i’m playing for a while but i don’t know how i can set up this navigation panel on the right side. could you explain the starting point? peter
i think it’s the pinbar class, but what i want to do is the colorpicker and other tools in this panel without windows….
Hi, how do I update the treeview icons? I end up with collapsing the entire tree and expanding it – but its messing up the scroll position. There is probably an easy way to do it, right?
@Peter, the PinBar component is not documented. You need to dig in the example code on your own.
@Lukas, the icons can be customized as described in the chapter Setting custom icons in this article.
Thanks for the reply. I set the icons and they update OK when I scroll the tree or collapse the parent node. But how can I update the tree without clicking it?
Could you please post example code that does not work?
Lukas did send me an email with example code. The problem he encountered was that the internal state of a data item (within the data source structure) has been changed and the tree still did not reflect that change. The solution is to let the data item dispatch an ordinary Event.CHANGE event in all cases the tree should perform data item depending operations such as determining and refreshing the current icon or displaying and updating the item label.
Hi Jens! In my case it was quite hard to change the data source structure. So I sticked to your other suggestion and used the ‘dirty’ way – assigned a new iconFunction to the style:
_tree.setStyle(DirectoryIcon.style.iconSkinFunction,
function(data: TreeNodeData):Class {
return iconFunction(data);
}
);
It’s not the optimal solution but works great as a global icon refresh.
Thanks for your support Jens!
Another thing – it would be nice if we could use the as3commons libraries instead of as3 libraries. It can get quite confusing to use org.as3commons.collections.framework.IDataProvider in one place and com.sibirjak.asdpc.core.dataprovider.IDataProvider in another.
The com.sibirjak.asdpc.core.dataprovider.IDataProvider is to be replaced by the as3commons one in the version 1 of this project.
Coming end of the year.
Great Job, you are not only engineer, but artist also… hope to see the final version.