Apache – How to change the appearance of nodes in a Tree control in Flex using an extended TreeItemRenderer

apache-flex

I'm using a tree control that I want to customize. The data items in the tree's dataProvider have a property name that should be used for labeling the node, and a property type that should be used to select one of several embedded images for use as an icon. The simplest way to do this is by using the labelField and iconFunction properties.

However, I wanted to get started with item renderers and open the door for adding more complex customization later, so I tried making my own item renderer. I extended the TreeItemRenderer class as follows and used it in my tree control:

class DirectoryItemRenderer extends TreeItemRenderer
{
  [Embed("assets/directory/DefaultIcon.png")]
  private static var _DEFAULT_ICON:Class;

  // ... some more icons ...

  override public function set data(value:Object):void
  {
    super.data = value; // let the base class take care of everything I didn't think of
    if (value is Node) { // only handle the data if it's our own node class
      switch ((value as Node).type) {
        // ... some case clauses ...
        default:
          this._vSetIcon(_DEFAULT_ICON);
      }
      this.label.text = (value as Node).name;
    }
  }

  private function _vSetIcon(icon:Class):void
  {
    if (null != this.icon && this.contains(this.icon)) {
      this.removeChild(this.icon);
    }
    this.icon = new icon();
    this.addChild(this.icon);
    this.invalidateDisplayList();
  }
}

This code has no effect whatsoever, icon and label in the tree control remain at their defaults. Using trace(), I verified that my code is actually executed. What did I do wrong?

Best Answer

Looking at the base mx.controls.treeClasses.TreeItemRenderer class, I see that in the updateDisplayList function the renderer gets it's icon and disclosureIcon classes from _listData:TeeListData. Instead of overriding the updateDisplayList function, try modifying the icon and disclosureIcon classes of the renderer's private _listData instance in your _vSetIcon method using the public accessors, like so:

private function _vSetIcon(icon:Class, disclosureIcon:Class = null):void
{
    var tmpListData:TreeListData;   

    if (disclosureIcon == null) disclosureIcon = icon;

    tmpListData = this.listData;
    tmpListData.icon = icon;
    tmpListData.disclosureIcon = disclosureIcon;

    this.listData = tmpListData;
}

EDIT

Here is some clarification on the difference between data and listData. You'll have to excuse my omission of package names but I'm editing from my phone so its tough to look them up and I don't know the package names off the top of my head. data is defined in the context of a TreeItemRenderer in the IDataRenderer interface. You create a data renderer by implementing this interface and defining a public property data, which in this case is set by the parent control and contains some data and meta-data from the dataProvider to be rendered by the data renderer class.

listData is defined in the IDropInListItemRenderer interface as a property of type BaseListData and is realized in the TreeItemRenderer class as a property TreeListData. It differs from the data property in that it contains meta-data that describes the TreeListRenderer itself (icon, indent, open) as well as (I believe, I'll have to double check this later) a reference to the data item being rendered. I gather that It's used by the the TreeItemRenderer and I would imagine the parent list control for display update and sizing purposes. Someone is free to correct or add onto that if I'm incorrect or missed something, I'm going of what I remember drom the code.

In this case, you wanted to use meta-data from the data set from the data provider to modify data that determines the display of the renderer, so you would need to modify both.

I think the real confusion here however came from the fact that you extended the TreeItemRenderer class then tried to override functionality on the component in a manner the original developer didn't intend for someone to do, hence the unexpected results. If your goal is education and not ease of implementation you would probably be better served by extending the UIComponent class and using the TreeItemRenderer code as a reference to create a class that implements the same interfaces. That would be a real dive into the pool of custom component development.