Archive for October, 2006

Tree Control DataProviders

Lately I’ve been thinking of trees. Maybe it’s because it is autumn in the northern hemisphere. So it seems an appropriate time to talk about Flex Tree controls. I’m going to publish a small series on the Flex 2.0 Tree component as there is a lot of ground to cover. In this series I’ll present information on Tree dataProviders, itemRenderers, and drag-and-drop.

Data Providers

The Tree uses heirarchical data. That is, data that has levels: nodes that are branches and leaves. The DataGrid, by contrast, presents non-heirarchical data as does the List control.

XMLListCollection is an excellent dataProvider for the Tree and is the most commonly used class for this purpose. You can also use ArrayCollection and I’ll go into more detail on that later in this article.

XML is a good because it is, by nature, heirarchical. Consider this simple XML:

[Bindable]
var company:XML =
<node>
    <node label="Finance" dept="200">
        <node label="John H" />
        <node label="Sam K" />
    </node>
    <node label="Engineering" dept="300">
         <node label="Erin M" />
        <node label="Ann B" />
    </node>
    <node label="Operations" dept="400" isBranch="true" />
 </node>

You can easily see the Tree structure in that XML. But also notice something else: all of the elements in the XML are <node /> elements. Every element is the same in that it has the name "node" and it has a label; branches and leaves as well. If you need to have a node that should be a branch but has no leaves, use isBranch="true" to tell the Tree the node should be treated like a branch.

The Tree likes this very much. You can give that XML to the Tree and you will see that structure without any fuss. But is this realistic to ask that all of the nodes in the data be the same? Consider this XML:

[Bindable]
var company:XML =
<list>
    <department title="Finance" code="200">
        <employee name="John H" />
        <employee name="Sam K" />
    </department>
    <department title="Engineering" code="300">
       <employee name="Erin M" />
       <employee name="Ann B" />
    </department>
    <department title="Operations" code="400" isBranch="true" />
</list>

This XML is not uniform. If you give this to a Tree component you will get the XML dumped in the control because the Tree doesn’t know what to do with it without extra instructions.

This XML can be displayed in the Tree with the help of a labelFunction.

private function treeLabel( item:Object ) : String
{
     var node:XML = XML(item);
     if( node.localName() == "department" )
         return node.@title;
     else 
         return node.@name;
}

The labelFunction simply returns the value of the name or title attribute depending on the type of node it is.

XMLListCollection

Earlier I mentioned XMLListCollection as a good dataProvider for the Tree, but I’ve been using XML so far. Here is the proper way to supply the Tree with data:

[Bindable]
var companyList:XMLListCollection = new XMLListCollection( company.department );

<mx:Tree dataProvider="{companyList}" labelFunction="treeLabel" />

An XMLListCollection is better as a dataProvider because the Tree can manipulate it (for editing, drag-and-drop, etc.) and because changes to the XMLListCollection are reflected in the Tree. That is, if I were to change the company XML object, you would not see the change in the Tree. If change the XMLListCollection, companyList, then not only would the underlying XML be changed, but so would the Tree.

Use XMLListCollection to supply the Tree with its data; you can change the collection and both the Tree and underlying XML will get changed, too.
If you cannot supply a very uniform XML structure to the Tree, use a labelFunction (or itemRenderer, but that’s coming later) to supply the label for the display.

ArrayCollection

You can also use an ArrayCollection for the Tree. You can make an ArrayCollection heirarchical by embedding one ArrayCollection inside another:

[Bindable]
private var companyData:ArrayCollection = new ArrayCollection(
[ {type:"department", title:"Finance", children:new ArrayCollection(
                                                        [ {type:"employee", name:"John H"},
                                                          {type:"employee", name:"Sam K"} ] ) },
   {type:"department", title:"Engineering",
children: new ArrayCollection(
                                                       [ {type:"employee", name:"Erin M"},
                                                         {type:"employee", name:"Ann B"} ] ) },
   {type:"department", title:"Operations",
children: new ArrayCollection()}
] );

With this structure you’ll notice that whenever a node has children, the name of the field is children – the Tree looks for this to identify branches from leaves.

You will also need a labelFunction with this data, too, so the Tree knows what to display on each node:

private function treeLabel( item:Object ) : String
{
     if( item.type == "department" )
         return item.title;
     else
         return item.name;
}

Adding Nodes

You make changes to the Tree through the dataProvider, not through the Tree control itself. When you want to add a node you add it to the dataProvider and the Tree will be changed automatically.

To add a node to the XMLListCollection you need to have a handle on the parent node. For example, to add a new department which is a top-level node, you can do that like this:

var newNode:XML = <department title="Administration" code="500" >
                                       <employee name="Mark C" />
                              </department>;
companyList.addItem(newNode);

Here is how to add a new employee to the existing Operations department:

var newNode:XML = <employee name="Beth T" />;
var dept:XMLList =company.department.(@title == "Operations");
if( dept.length() > 0 ) {
     dept[0].appendChild(newNode);
}

Once you identify a specific node and have its XML equivalent, you can use the appendChild() method.

To add a node to the ArrayCollection you just append it to whatever part of the structure requires the node. Here’s how to add a new department (top-level) node:

var newNode:Object = {type:"department",title:"Administration"};
var newEmployee:Object = {type:"employee",name:"Mark C"};
newNode.children = new ArrayCollection( [newEmployee] );
companyData.addItem(newNode);

Here is how to add a new employee to the existing Operations department:

var newNode:Object = {type:"employee", name:"Beth T"};

for(var i:Number=0; i < companyData.length; i++) {
     var item:Object = companyData.getItemAt(i);
     if( item.title == "Operations" ) {
         var children:ArrayCollection = item.children;
         children.addItem(newNode);
         companyData.itemUpdated(item);
         empName.text = "";
         break;
     }
}

As you can see, using an ArrayCollection to add a node is a bit more complicated than using XML.

Removing Nodes

If you know you are removing a top-level node you can do that through the XMLListCollection’s removeItemAt() method – but you have to know the index of the item. In the following example, all you know is the name, "Operations", so you have to loop through the nodes and when a match is found, remove the item.

var deptTitle:String = "Operations";
for(var i:Number=0; i < companyData.length; i++) {
     var item:XML = XML(companyData.getItemAt(i));
     if( item.@title == deptTitle ) {
         companyData.removeItemAt(i);
         break;
     }
}

Removing the selected top-level node little easier:

var index:Number = tree.selectedIndex;
companyData.removeItemAt(index);

Here is how to remove a leaf node:

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];
     }
}

The same technique applies for ArrayCollection in that you have to search for the item, but once you find it you can use the ArrayCollection removeItemAt() method since the index is always valid for an ArrayCollection.

Here’s how to remove a leaf node if the Tree’s dataProvider is an ArrayCollection:

var node:Object = tree.selectedItem;
if( node == null ) return;
if( node.type != "employee" ) return;

var children:ArrayCollection = node.parent().children() as ArrayCollection;
for(var i:Number=0; i < children.length; i++) {
     if( children[i].name == node.name ) {
         children.removeItemAt(i);
         break;
     }
}

t is not possible to give a Collection a node of the Tree data and have the Collection remove it – you must hunt for it and delete it yourself.

Next Time

The next article will cover drag and drop – within a tree, from a tree, and onto a tree. Stay tuned.

Post-MAX Apollo On-Line Seminar – " Understanding Apollo"

If you were not able to attend MAX, or just want more information on Apollo, please join Mike Chambers in a free 1 hour on-line seminar. During this session, attendees will learn about Adobe’s next-generation run-time client that will extend the reach and capabilities of today’s Rich Internet Applications. The seminar is scheduled on Thursday, November 2nd at 3:00 p.m. US/Eastern.  Registration link: http://www.adobe.com/cfusion/event/index.cfm?event=detail&id=648909&loc=en_us

dataTip Tip

Here’s how to prevent a dataTip from appearing.

Say you are using dataTips in a DataGrid. For the most part the dataTip shows useful information. But sometimes a tip gets shown that’s not so useful. Perhaps the tip is blank or zero. You’ll still get the tip pop-up, but just a small box.

If you add a dataTipFunction you can return null and the dataTip will not be shown:

private function interestRateTip( item:Object ) : String {
     if( item.principle == 0 ) return null;
     else return item.rate;
}

MAX2006

If you haven’t registered for MAX yet, you have until Oct 16.

I was sent the following information about MAX2006 which I’ll share with you. This should be a really exciting show.

MAX legacy

I have heard some rumblings that this MAX may not be the same as previous MAX events and I’d like offer an alternative point of view.  I expect this years’ event to exceed the expectations of our audience, not only in terms of the content and execution, but the new opportunities that are emerging for our developer community (ie Apollo, Flex/Livecycle, mobile, etc).  The event is comprised of essentially the same ingredients as in years past and a lot of the same folks are working to make this the best MAX ever.  

I’m not sure how widespread this is (I suspect not very wide as we have reached the equivalent attendance as last year), but I did want to state that Adobe is committed to MAX in more ways than just holding an event.  

Keynotes

In the day 1 keynote, you’ll see Shantanu Narayen and Kevin Lynch, along with a cast of other folks demonstrate the Adobe Engagement Platform.  You’ll see demos of both current and future technologies, including the anticipated Apollo release of our client platform.  Don’t miss the opening experience which will remain a secret until show time.  

In the day 2 keynote, Al Ramadan will share what’s happening with Adobe’s mobile solutions around the world, including some updates that will turn some heads in the US market.  Adobe’s CEO, Bruce Chizen will present the annual MAX Awards, a celebration of the incredible work being done in the community today.  There will also be some fun and surprises in this session as well.

Sneak peeks

While the sneaks haven’t been finalized, there will be around 10 sneaks from the engineering teams that are exclusive to MAX, and will not be broadcasted outside of this event.  Our Labs community is growing, and we’ll be showing some things that won’t even be on Labs for some time, along with a few that will be heading there very soon.

What’s more?

Tons of networking opportunities, the annual MAX party, Las Vegas, talking about frameworks, YouTube – who would want to miss this?

DataGrid Tip: Row Background Color

How to change the background color of a row is an often-asked question. In Flex 2.0 it is easy. Follow these steps:

  1. Create a new class that extends mx.controls.DataGrid. This class can be an MXML file or an ActionScript file, whichever you are most comfortable with.
  2. Overide the protected function, drawRowBackground:

        override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number,
                                                       color:uint, dataIndex:int):void
      
    {
           
    // make any tests here, then change color accordingly. For example: color = 0xFF0000;
            // call the super function to make it happen.
           
    super.drawRowBackground(s,rowIndex,y,height,color,dataIndex);
       
    }

  3. Now use your new grid in place of the <mx:DataGrid> in your application.

Within drawRowBackground you can test your data. The dataIndex argument can be used to look up the item in the dataProvider that corresponds to this row. For example, suppose you want to color any row green whose quantity is greater than 1000:

var item:Object = (dataProvider as ArrayCollection).getItemAt(dataIndex);
if( item.quantity > 1000 ) color = 0x00FF00;

It’s just that easy.

Fall News

Wow, it’s been a month since my last post. Time sure has flown by. It has been a busy September as more and folks are signing up and using Flex 2. The feedback so far has been very positive.

I’ll be adding more to this blog this month, so stay tuned. But I did want to make you aware of some things:

New Flex 2 Components

Check out new Flex 2 components by visiting the Flex Team Page. There’s a new auto-complete text input control you’ll love to have in your applications.

MAX2006

It’s not too late to sign up for MAX2006 – it’s happening in Las Vegas. While at MAX, be sure to stop by the Support Desk on the show floor and get your questions answered. MAX is a great opportunity for you to met the talent behind the products – take advantage of it!

Flash Player 9 Updater Beta 1

Direct from Emily:
The Flash Player 9 Update 1 beta just went live on Adobe Labs. The update includes bug fixes, support for Windows Vista, and one cool new feature … full-screen mode in the web players. The wiki article contains information on what you need to do to implement and enable this feature using the beta player. There is a zip file that contains publishing templates for Flex and Flash, new class files and sample code.

Emily’s blog post: http://weblogs.macromedia.com/emmy/archives/2006/10/flash_player_9_3.cfm
Labs Page: http://labs.adobe.com/technologies/flashplayer9/
Release notes listing publicly reported issues we have fixed: http://labs.adobe.com/technologies/flashplayer9/releasenotes.html
Full Screen Wiki article: http://labs.adobe.com/wiki/index.php/Flash_Player:9:Update:Full-Screen_Mode