Wednesday, July 8, 2009

Communication between itemrenderers

ItemRenderers has always been my favourite feature in flex. In one of the tasks I took on, I had to communicate between item renderers to achieve the desired result.

As usual, flex's event architecture was there to aid me achieve this task.

So here is the flow

1.Dispatch an event with bubbles set to true when some user action is performed in the source item renderer or in caase of an inline item renderer call a method using outerDocument like this.
outerDocument.handleFirstColumnChange(data);

2.Have a listener attached to the event dispatched by the source renderer on the parent.Now the parent should re-dispatch the event on the datagrid like
datagrid.dispatcEvent(new Event("itemchangeonfirstcolumn"),data.id));
Note:I am passing the id along with this event so that the target itemrenderers can figure out if they are intended to react or not.

3.Add a listener on the target itemrenderer and on the handler you can do whatever the target is supposed to do.

Here is a simple example The case is that one itemrenderer has to change itself based on a selection done on the other item renderer.

Right clik->viewsource for full source code of the example.

Enjoy!



Bookmark and Share

Friday, April 3, 2009

Changing the headers of dataGrid dynamically

In a recent project I was working on, I came across a situation where I will know the header text for the columns at run time, pulled from a xml document.

And here is how I did it.


private function setHeaderTexts():void {
var i:Number = 0;
var columns:ArrayCollection =new ArrayCollection(datagrid.columns);
for each (var column:DataGridColumn in columns) {
column.headerText = tempArrayCollection.getItemAt(i).toString();
//tempArrayCollection is where I store the list of headers from the XML
i++;
}
datagrid.invalidateDisplayList();
}


Hope it helps!

Bookmark and Share

Wednesday, April 1, 2009

Refresh tileList on DataChange

I happened to come across an issue with tileLists that use a custom itemRenderer.

The problem is that when I change the data provider, the items in the tileList do not redraw appropriately.

After some time spent in digging into this, I found out that for some reason the dataChange event in the itemrenderer is not triggered automatically when the data provider changes.

Note:DataChange event is triggered internally for dataGrids which are bound to a dataProvider, so we wont even notice it,but that is not the case for a tileList.

The event is triggered only when we call tileList.invalidateList().

So here is what I did. Call tileList.invalidateList() every time I change the dataProvider and handle the dataChange event in the itemRenderer and reset the UIControl's properties whatever it may be.

Here is what the view does.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;

[Bindable]
private var dataP:ArrayCollection = new ArrayCollection(
[{isMarried:true, isAnimal:true,name:"Montgomery"},
{isMarried:false, isAnimal:true,name:"Juneau"},
{isMarried:true, isAnimal:false,name:"Little Rock"},
{isMarried:true,isAnimal:true,name:"Ricky Martin"}]);

private function changeFirstElement():void {
dataP.getItemAt(0).name = "changed";
tileList.invalidateList();
}

]]>
</mx:Script>

<mx:TileList itemRenderer="testItemRenderer" dataProvider="{dataP}"
id="tileList"/>
<mx:Button label="change first element" click="changeFirstElement();"/>
</mx:Application>


And here is what the itemRenderere does.

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" dataChange="handleDataChange()"
creationComplete="handleCreationComplete()">
<mx:Script>
<![CDATA[
[Bindable]
private var theName:String=new String();

private function handleCreationComplete():void {
if (data != null && data.hasOwnProperty("name")) {
theName =data.name;
}
}
private function handleDataChange():void {
if (data != null && data.hasOwnProperty("name")) {
if (theLabel) {
theName = data.name;
}
}
}
]]>
</mx:Script>
<mx:Label text="{theName}" id="theLabel"/>
</mx:Canvas>


Hope it helps!




Bookmark and Share

Reuse ItemRenderers for Multiple columns

When dealing with item renderers, one will come across all sorts of problems thrown at . One such case is "how to reuse the same itemrenderer"

Picture this case. You have a custom itemrenderer,( imagine a check box within a canvas for now) that performs a specific task and you want to reuse it on multiple columns. But how will the itemrenderer know which field in the arrayCollection is to be used to render itself appropriately?

Alright. Here is the trick.

There is this interface called IDropInListItemRenderer which your itemrenderer will have to implement to get access to listData which can be typeCasted to DataGridListData. The listData variable will hold reference to the owner of the itemRenderer and the hierarchy upwards. Following is the list of steps one has to follow.

1.Create the itemRenderer class that implements the IDropInListItemRenderer.

2.Create an instance variable to buffer the listData , namely _listData.

3.Override the set/get methods of the listData instance variable . In the setter, buffer the listData onto the instance variable

Ex:


private var _listData:DataGridListData;
public function set listData(value:BaseListData):void
{
_listData = DataGridListData(value);
}


Note:The importance of this step is that the itemrenderer can use the _listData variable to find out the name of the dataField , using something like _listData.dataField. Once the name of the dataField is acquire, the value for that datfield can be acquired by using data[_listData.dataField]

4.Override the updateDisplyList() method to update the status of the checkBox based on the value of the dataField.

Ex:

override protected function updateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void {

super.updateDisplayList(unscaledWidth,unscaledHeight);
_instanceCheckBox.selected = Boolean(data[_listData.dataField]);
}

5. Create the datagrid and use the above created item renderer.
Ex:

<mx:DataGridColumn dataField="isMarried" itemRenderer="sampleItemRenderer"/>
<mx:DataGridColumn dataField="isAnimal" itemRenderer="sampleItemRenderer" />




Following is the complete code.

The view:

// View
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var dataP:ArrayCollection = new ArrayCollection(
[{isMarried:true, isAnimal:true,name:"Montgomery"},
{isMarried:false, isAnimal:true,name:"Juneau"},
{isMarried:true, isAnimal:false,name:"Little Rock"},
{isMarried:true,isAnimal:true,name:"Ricky Martin"}]);
]]>
</mx:Script>



<mx:DataGrid dataProvider="{dataP}">
<mx:columns>
<mx:DataGridColumn dataField="isMarried" itemRenderer="sampleItemRenderer"/>
<mx:DataGridColumn dataField="isAnimal" itemRenderer="sampleItemRenderer" />
<mx:DataGridColumn dataField="name"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>



The itemRenderer


package
{
import mx.containers.Canvas;
import mx.controls.CheckBox;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;

public class sampleItemRenderer extends Canvas implements IDropInListItemRenderer
{
private var _listData:DataGridListData;
private var _instanceCheckBox:CheckBox = new CheckBox();
private var _currentDataFieldName:String = new String();
public function sampleItemRenderer()
{
super();
}

public function get listData():BaseListData
{
return _listData;
}

public function set listData(value:BaseListData):void
{
_listData = DataGridListData(value);
}
override protected function createChildren():void {
super.createChildren();
addChild(_instanceCheckBox);

}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth,unscaledHeight);
_instanceCheckBox.selected = Boolean(data[_listData.dataField]);
}
}
}

Hope it helps!



Bookmark and Share

Tuesday, March 31, 2009

Filterfunctions .

Here is a quick little post on how to use filter functions .

The primary use of filterfunctions as it implies is to filter data on a data structure such as ArrayCollection which will in turn filter the data displayed on UI controls like datagrid,combobox.

The filterfunction is called with each item in the arrayCollection as a paramter. Say you have n items in the arrayCollection, the filter function will be called n times with items 0...n passed in as a parameter. The items for which the call returns true are added to the visible list, while the rest are ignored.

Lets jump in to the sample code.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var dataP:ArrayCollection = new ArrayCollection(
[{type:"1", data:"Montgomery",label:"Montgomery"},
{type:"2", data:"Juneau",label:"Juneau"},
{type:"1", data:"Little Rock",label:"Little Rock"},
{type:"2",data:"test 2",label:"test 2"}]);

private function handleOptionSelect() :void {
if (selectionCombo.selectedItem.toString() == "ALL") {
dataP.filterFunction = null;
} else {
dataP.filterFunction = filterOnTypes;
}
dataP.refresh();
}

private function filterOnTypes(item:Object):Boolean {
if (item.hasOwnProperty("type")) {
if (item.type == selectionCombo.selectedItem.toString()) {
return true;
}
}
return false;
}


]]>
</mx:Script>

<mx:ComboBox change="handleOptionSelect()" id="selectionCombo">
<mx:dataProvider>
<mx:ArrayCollection>
<mx:Object>ALL</mx:Object>
<mx:Object>1</mx:Object>
<mx:Object>2</mx:Object>
</mx:ArrayCollection>
</mx:dataProvider>
</mx:ComboBox>

<mx:DataGrid dataProvider="{dataP}">
<mx:columns>
<mx:DataGridColumn dataField="type"/>
<mx:DataGridColumn dataField="data"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>



Note that the elements in the arrayCollection that do not match the filter criteria removed from the arrayCollection. You can restore the original data set by setting the filter function to null and calling the refresh method again.

Wondering how the AVM knows the original set? it just stores the original data set in the "source" attribute of the arrayCollection.

Hope it helps!


Bookmark and ShareBookmark and Share

Friday, January 9, 2009

Complex editors on a datagrid

There are times when you want to have a data grid whose cells can have other complex controls like combobox , numeric stepper.To achieve this multi-entity ,entry form t, we use the inline itemEditor field of the datagrid component.

The key here is the itemEditor property of the datagrid column to which you can specify a class ,aka editor or renderer(either flex built in or custom). Now how will flex know which property of the editor or renderer is the user input? . The editorDataField” property specifies which property of the renderer is to be taken as the user input. Example: value property of a numeric stepper, when numeric stepper is the itemEditor.

One very important thing to notice here is that the dataprovider is also synched with the values committed on the item renderers.

Here is an example that shows how to do this.



<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">

<mx:Script>

<![CDATA[

private function showArrayCollection():void {

resultText.htmlText="";

for (var i:Number=0;i<stateArray.length;i++) {

resultText.htmlText+="Name:"+stateArray.getItemAt(i).name;

resultText.htmlText+=" Age:"+stateArray.getItemAt(i).age;

resultText.htmlText+="<br>";

}

}

]]>

</mx:Script>

<!--

The data provider for the datagrid.The age property will be synched with the user's input,

when he selects it from the age property.

!-->

<mx:ArrayCollection id="stateArray">

<mx:Object name="Amitha" age="25"/>

<mx:Object name="Priyanka" age="27"/>

<mx:Object name="Krups" age="45"/>

</mx:ArrayCollection>

<mx:DataGrid x="531" y="82" editable="true" dataProvider="{stateArray}">

<mx:columns>

<mx:DataGridColumn headerText="Name" dataField="name" editable="false"/>

<mx:DataGridColumn headerText="Age" dataField="age" editable="true"

itemEditor="mx.controls.NumericStepper" editorDataField="value"/>

</mx:columns>

</mx:DataGrid>

<mx:Button label="show" click="showArrayCollection()">

</mx:Button>

<mx:TextArea width="300" height="300" id="resultText">

</mx:TextArea>

</mx:Application>

Hope it helps!!

Monday, January 5, 2009

Events for DUMMIES

Accept it, we all have used events but never really got to know what goes behind the scenes.

So here in vivid little words, I try to throw some light on this.

Here goes my “EVENTS for DUMMIES!”

Flash Player 9 implements an event model based on the World Wide Web Consortium’s (W3C) specification entitled Document Object Model Events available at http://www.w3.org/TR/DOM-Level-3-Events/events.html. According to this document, the lifecycle of an event that deals with display objects consists of three phases: capture, target, and bubbling.

You have probably heard of these names but never got to know why three phases are used??

The reason is simple.W3C recommends so! J

Capture: During this phase, Flash Player makes a first pass to check every object from the root of the display list to the target component to see if any parent component might be interested in processing this event. By default, events are ignored by the parents of the target component at the capture phase. Its like collecting who and all are interested in the a components events.

Ex:

If component 1 has a code like component2.addEventListener(“eventname”,myfunction);

Component 1 will be added to a list in this phase.

Target: At this phase, event object properties are set for the target and all registered event listeners for this target will get this event.

Event.name and event.whatever is set now!

Bubbling: Finally, the event flows back from the target component all the way up to the root to notify all interested parties identified during the capture phase. Not all events have a bubbling phase and you should consult the AS3 language reference for the events you’re interested in.

The three event phases described above don’t apply to the user-defined events because Flash Player 9 doesn’t know about parent-child relations between user-defined event objects. But AS3 developers can create custom event dispatchers, if they want to arrange event processing in three phases.

Hope it helps!