Here is the MXML itemRenderer from the previous article used for a TileList. I'm going to make it bit more dynamic by having it react to changes from an external source (I called this file BookItemRenderer.mxml):
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="250" height="115" >
<mx:Script>
<![CDATA[
]]>
</mx:Script>
<mx:Image id="bookImage" source="{data.image}" />
<mx:VBox height="115" verticalAlign="top" verticalGap="0">
<mx:Text text="{data.title}" fontWeight="bold" width="100%"/>
<mx:Spacer height="20" />
<mx:Label text="{data.author}" />
<mx:Label text="Available {data.date}" />
<mx:Spacer height="100%" />
<mx:HBox width="100%" horizontalAlign="right">
<mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]">
<mx:click>
<![CDATA[
var e:BuyBookEvent = new BuyBookEvent();
e.bookData = data;
dispatchEvent(e);
]]>
</mx:click>
</mx:Button>
</mx:HBox>
</mx:VBox>
</mx:HBox>
Suppose you are showing a catalog of items in a TileList. You also have a Slider (not part of the itemRenderer) that lets the user give a range of prices; all items that fall outside of the range should fade out (the itemRenderers' alpha value should change). You need to tell all the itemRenderers that the criteria has changed so that they can modify their alpha values.
Your override of set data might look something like this:
override public function set data( value:Object ) : void
{
super.data = value;
if( data.price < criteria ) alpha = 0.4;
else alpha = 1;
}
The question is: how to change the value for criteria? The "best practice" for itemRenderers is to always have them work on the data they are given. In this case, it is unlikely, and impractical, to have the test criteria be part of the data. So that leaves a location outside of the data. You've got two choices:
For me, the choice is the first one: extend a class and make the criteria part of that class. After all, the class is being used to display the data, the criteria is part of that display. For this example, I would extend TileList and have the criteria as a public data member.
package
{
import mx.controls.TileList;
public class CatalogList extends TileList
{
public function CatalogList()
{
super();
}
private var _criteria:Number = 10;
public function get critera() : Number
{
return _criteria;
}
public function set criteria( value:Number ) : void
{
_criteria = value;
}
}
}
The idea is that a control outside of the itemRenderer can modify the criteria by changing this public property on the list control.
The itemRenderers have access to another piece of data: information about the list itself and which row and column (if in a column-oriented control) they are rendering. This is known as listData and it could be used like this in the BookItemRenderer.mxml itemRenderer example:
override public function set data( value:Object ) : void
{
super.data = value;
var criteria:Number = (listData.owner as MyTileList).criteria;
if( data.price < criteria ) alpha = 0.4;
else alpha = 1;
}
Place this code into the <mx:Script> block in the example BooktItemRenderer.mxml code, above.
The listData property of the itemRenderer has an owner field, which is the control to which the itemRenderer belongs. In this example, the owner is the MyTileList control—my extension of TileList. Casting the owner field to MyTileList allows the criteria to be fetched.
Access to listData is available when the itemRenderer class implements the IDropInListItemRenderer interface. Unfortunately, UI container components do not implement the interface that gives access to the listData. Control components such as Button and Label do, but for containers you have to implement the interface yourself.
Implementing this interface is straightforward and found in the Flex documentation. Here's what you have to do for the BookItemRenderer class:
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" ... implements="mx.controls.listClasses.IDropInListItemRenderer">
set and get functions to the <mx:Script> block in the itemRenderer file.
import mx.controls.listClasses.BaseListData;
private var _listData:BaseListData;
public function get listData() : BaseListData
{
return _listData;
}
public function set listData( value:BaseListData ) : void
{
_listData = value;
}
When the list control sees that the itemRenderer implements the IDropInListItemRenderer interface, it will create a listData item and assign it to every itemRenderer instance.
Setting the criteria in my class isn't as simple as assigning a value. Doing that won't tell the Flex framework that the data has been changed. The change to the criteria must trigger an event. Here's the modification to the set criteria() function:
public function set criteria( value:Number ) : void
{
_criteria = value;
invalidateList();
}
Notice that once the _criteria value has been set, it calls invalidateList(). This causes all of the itemRenderers to be reset with values from the dataProvider and have their set data functions called.
The process then looks like this: