Posts Tagged ‘Tree’

How to combine the power of a Flex Tree control within a ComboBox

// July 22nd, 2009 // 15 Comments » // Flex framework

I’ve found a good example of how to nest a Flex Tree into a ComboBox with only one issue: the selected tree node wasn’t shown once you close and open back the combo box. So I’ve decided to rewrite the component from scratch trying to remove that issue. The result is shown below.

Here is the mxml code of the example shown above.

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
<?xml version="1.0"?> 
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:wg="com.webgriffe.components.*" 
    creationComplete="loadXML()"> 
 
  <mx:Script> 
  <![CDATA[ 
      import mx.collections.XMLListCollection; 
      import mx.rpc.events.ResultEvent; 
      import mx.rpc.http.mxml.HTTPService; 
 
      public var xmlService:HTTPService = new HTTPService(); 
 
      [Bindable]
      public var xmlResult:XML;
 
      [Bindable] 
      public var xmlList:XMLList;
 
      [Bindable] 
      public var xmlTeams:XMLListCollection; 
 
    public function loadXML():void 
    { 
      xmlService.url = "mlb.xml"                 
      xmlService.resultFormat = "e4x"; 
      xmlService.addEventListener(ResultEvent.RESULT, resultHandler); 
      xmlService.send(); 
    } 
 
    public function resultHandler(event:ResultEvent):void 
      { 
         xmlResult = XML(event.result); 
         xmlList = xmlResult.league; 
         xmlTeams = new XMLListCollection(xmlList); 
      } 
  ]]> 
  </mx:Script> 
  <wg:TreeComboBox 
      width="200" treeHeight="250" 
      dataProvider="{xmlTeams}"
      labelField="@label" />
</mx:Application>

To implement a Flex Tree within a ComboBox we have to reassign the default ComboBox dropdownFactory. By default it’s a List control so we assign it a customized Tree control and add some code to let both the components know each other in order to bind their behaviours. By binding behaviours i mean:

  • When you open the ComboBox, the Tree must be expanded in order to show the selected node.
  • When you select a node in the Tree, the ComboBox text should be assigned the selected node’s label.

Click here to download a zip file with the source files.

I wan to greet the authors of the following articles which represented the starting point of my work:

Enjoy!

How to add a line separator in a Flex Tree component

// May 8th, 2009 // No Comments » // Flex framework

Ever wanted to use a line separator in a Tree? It’s all a matter of customizing the Flex Tree and TreeItemRenderer and you can obtain what is shown in the example below.

Here are the steps to implement it:

  • define a property in your model to indicate that an item is a separator;
  • implement a customized TreeItemRenderer which stores a flag to keep track about the fact the node is a separator. This flag is used in some overridden methods to handle icon display and line drawing;
  • implement a customized Tree control which overrides some methods to avoid separator to be selected.

Let’s start by defining a property which indicates that an item in a tree is a separator. I keep things simple here and use a code snippet inspired by the Adobe Flex3 Component Explorer in MyTree.mxml:

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
<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:my="my.controls.*"
    layout="vertical" backgroundColor="#777777" 
    viewSourceURL="srcview/index.html">
  <mx:XMLList id="treeData">
    <node label="Mail Box">
      <node label="Inbox">
        <node label="Marketing"/>
        <node label="Product Management"/>
        <node label="Personal"/>
      </node>
      <node label="Outbox">
        <node label="Professional"/>
        <node label="Personal"/>
      </node>
      <node separator="true" />
      <node label="Spam"/>
      <node label="Sent"/>
    </node> 
  </mx:XMLList> 
  <my:MyTree 
      width="100%" height="100%"
      dataProvider="{treeData}"
      labelField="@label" />
</mx:Application>

You can notice that to indicate a node is a separator I simply use a separator XML attribute whose value is set to true.

The second step consists of implementing a MyTreeItemRenderer class which extends TreeItemRenderer and declares a isSeparator property used to store information about whether the current node is a separator.

I then override the set data method in order to store isSeparator value as shown in the code below taken from MyTreeItemRenderer.as:

102
103
104
105
106
107
108
109
override public function set data(value:Object):void
{
  super.data = value;
 
  // Check whether current node is a separator
  isSeparator = (value != null && value.@separator != null
    && "true" == String(value.@separator).toLowerCase());
}

I also need to avoid icon display in separator nodes, so I have to set the visibility of the icon to false. I can’t do that in the set data method because in component creation life cycle visibility property is not yet available in that method. So I override the commitProperties method in the following way (code from MyTreeItemRenderer.as):

115
116
117
118
119
120
121
122
override protected function commitProperties():void
{
  super.commitProperties();
  if (isSeparator)
  {
    icon.visible = false; 
  }
}

To finish TreeItemRenderer customization I have to override the updateDisplayList method in order to draw a line in case the node is a separator. To do that I don’t reinvent the wheel and simply copy the algorithm from the HRule component. You can substitute you own drawing algorithm if you don’t like that. Here is the code of updateDisplayList method taken from MyTreeItemRenderer.as:

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
override protected function updateDisplayList(
    unscaledWidth:Number, unscaledHeight:Number):void
{
  // Get graphics
  var g:Graphics = graphics;
  g.clear();
 
  // Call super method
  super.updateDisplayList(unscaledWidth, unscaledHeight);
 
  // Draw the line if current node is separator
  if (isSeparator)
  {
    //
    // Code taken from the HRule drawing algorithm.
    //
    ...

The TreeItemRenderer customization is almost done. At this point the separator is displayable in the tree but I still need to prevent mouse hovering and selection. To to that I implement a MyTree class which extends Tree. The first step consists of associating the MyTree ItemRenderer to the itemRenderer property of the MyTree class. I do that in class constructor of MyTree.as:

25
26
27
28
29
30
public function MyTree()
{
  super();
 
  itemRenderer = new ClassFactory(MyTreeItemRenderer);
}

Then I have to override both mouseOverHandler and mouseDownHandler methods in order to avoid node to be highlighted and selected. Here is the code from MyTree.as:

41
42
43
44
45
46
47
48
override protected function mouseOverHandler(event:MouseEvent):void 
{
 var item:IListItemRenderer = mouseEventToItemRenderer(event);
 if (item != null && !MyTreeItemRenderer(item).isSeparator) 
 {
        super.mouseOverHandler(event);
 }
}
53
54
55
56
57
58
59
60
override protected function mouseDownHandler(event:MouseEvent):void 
{
 var item:IListItemRenderer = mouseEventToItemRenderer(event);
 if (item != null && !MyTreeItemRenderer(item).isSeparator) 
 {
        super.mouseDownHandler(event);
 }
}

That’s it! You can download the full source code here or by right clicking on the example you find on the top of the page. I hope this helps you. Enjoy Flex coding!