Thursday, December 11, 2008

Add and remove lineseries - Flex LineChart

This took me some time to figure out so I thought I would post about it.

Main objective is to toggle on and off lineseries based on user input. Usually this would mean editing the dataprovider, tricky thing here is that I need the color of each line to always be the same based on data, so instead I leave the dataprovider alone and actually add and remove LineSeries to/from the LineChart. This probably isn't the best way to do this, but hey, it works, and it may help someone else out.

Here we go...

First lets start with the MXML:
Here I have a Panel with a LineChart and a Legend inside it, as well as 3 Toggle Buttons. You will notice that there are no series inside the LineChart.

<mx:Panel title="LineChart Example" height="500" width="100%" layout="vertical">

<mx:HBox id="choices" horizontalGap="0">
<mx:Button id="Profit" label="Profit" toggle="true" click="updateChart(event)"/>
<mx:Button id="Expenses" label="Expenses" toggle="true" click="updateChart(event)"/>
<mx:Button id="Amount" label="Amount" toggle="true" click="updateChart(event)"/>
</mx:HBox>

<mx:LineChart id="linechart" height="80%" width="100%" showDataTips="true" dataProvider="{expensesAC}" creationComplete="init()">

<mx:horizontalAxis>
<mx:CategoryAxis categoryField="Month"/>
</mx:horizontalAxis>

</mx:LineChart>

<mx:Legend dataProvider="{linechart}" labelPlacement="right" verticalGap="2" direction="vertical"/>

</mx:Panel>


And here is the DataProvider:

[Bindable]
private var expensesAC:ArrayCollection = new ArrayCollection( [
{ Month: "Jan", Profit: 2000, Expenses: 1500, Amount: 450 },
{ Month: "Feb", Profit: 1000, Expenses: 200, Amount: 600 },
{ Month: "Mar", Profit: 1500, Expenses: 500, Amount: 300 },
{ Month: "Apr", Profit: 1800, Expenses: 1200, Amount: 900 },
{ Month: "May", Profit: 2400, Expenses: 575, Amount: 500 } ]);


For ActionScript, lets start with the UpdateChart method:

This method looks to see if the button you just pressed is selected (toggle on) or not. If the toggle is on that means the user is adding the line, if the toggle is off that means the user is removing the line, and this method calls the appropriate method.

private function updateChart(evt:Event):void
{
if ( evt.currentTarget.selected == true )
{
addToChart(evt.currentTarget.label);
} else {
removeFromChart(evt.currentTarget.label);
}
}


Here is the addToChart method. This method creates a new LineSeries and adds it to the chart. Note that the LineSeries DataProvider is the same of the LineChart and the data you're referring to (yField) should already be present in the DataProvider.

private function addToChart(item:String):void
{
var newLS:LineSeries = new LineSeries();
var newStroke:Stroke = new Stroke();
newStroke.color = strokeColor(item);

newLS.yField = item;
newLS.displayName = item;
newLS.setStyle('form','curve');
newLS.dataProvider = expensesAC;
newLS.setStyle('lineStroke',newStroke);
var tmp:Array = linechart.series;
tmp.push(newLS);
linechart.series = tmp;
linechart.invalidateSeriesStyles();
}

You may have noticed the line newLS.setStyle('lineStroke',newStroke);. This refers to the following method, all it does is assigns a color based on the button selected.

private function strokeColor(item:String):uint
{
switch (item)
{
case 'Amount':
return 0x0000FF;
break;
case 'Profit':
return 0xFF0000;
break;
case 'Expenses':
return 0x00FF00;
break;
default:
return 0x000000;
break;
}
}


And finally here is the remove method.

private function removeFromChart(item:String):void
{
var objToRemoveIndex:int;
for ( var i:int = 0; i < linechart.series.length; i++ )
{
if ( linechart.series[i].yField == item )
{
objToRemoveIndex = i;
}
}

var tmp:Array = linechart.series;
tmp.splice(objToRemoveIndex,1);
linechart.series = tmp;
linechart.invalidateSeriesStyles();
}


The default series gets added with the CreationComplete method:

private function init():void
{
addToChart('Amount');
Amount.selected = true;
}


Anyway, I hope that wasn't too confusing. I should really start hosting these little examples somewhere with the ViewSorce available. If you need more clarification just gimmie a comment here.

14 comments:

Deiviz said...

Thanks Ang, you save my life!!!. Nice works.

Saludos desde Madrid !!!

Navis Michael Bearly said...

Awesome amazing!!!. Thanks indeed

Andriy said...

actually i never write a comments..but this post is very helpful. Thanks!

steve h said...

Very useful - thanks for that.

One note: for me at least, it gets a bit buggy when I try to deselect 'Amount' (the line that is pre-selected). But if I remove it from the init() method it works fine.

steve h said...

Ignore that last bit about the bug - I fixed it. Somehow creationComplete="init()" ended up in the wrong place. Now I just need to find someone to blame for that... ;)

thanks!

Vengu said...

Good work and you so pretty :-)

Anonymous said...

Must the dataProvider be in arrayCollection?Can it be in xmlListCollection?If can, how do you do it?

Navis Michael Bearly said...

yeah!! you can do it XMLListCollection too.

Anonymous said...

How can I do this when my data are in external xml?

bahattin said...

Very useful information. Thanks for sharing

Anonymous said...

Thanks...that really helped!!

Anonymous said...

Thanks! It's really helpful!

Anonymous said...

you need if(!objToRemoveIndex) return; after the loop.

Saber Chebka said...

Thanks for the post, it helped me.