tags:

views:

279

answers:

2

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?

A: 

I'd probably try something simple, as in this example from the Adobe Cookbooks. I notice that they override updateDisplayList, which may have something to do with your problems.

There's another example (for Flex 2, but looks applicable to Flex 3) that shows how to manage the default icons. It looks like you'll want to manage the icon yourself, setting the default icon styles to null, instead of trying to manipulate the superclass's icon property.

Update -- Looking at the source for TreeItemRenderer, commitProperties has the following before checking the data and setting up the icon and label:

if (icon)
{
    removeChild(DisplayObject(icon));
    icon = null;
}

Also, it looks like the setter for data calls invalidateProperties. Hence, your icon is wiped out when the framework gets around to calling commitProperties.

CapnNefarious
I had seen the second example before. My problem is that it works around what I'm interested in. For example, I can't see why they don't work with `icon`, as this must be still lurking around somewhere in the superclass. It's probably `null` by virtue of having set the style properties, but there must be two children now that can hold an icon. Guess I have to go read source...
Hanno Fietz
I'm now guessing the original `updateDisplayList` sets the icon, so when I set it in `set data`, it got rewritten afterwards.
Hanno Fietz
A: 

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.

Ryan Lynch
I don'r really get the difference between `listData` and `data`, i. e. when to use what, do you?
Hanno Fietz