March 10th, 2008 by Kyle
Tags: charting, Flex, interactive series, series
Posted in: Flex
This sample basically builds upon another sample I posted a while back.
It is pretty self explanatory – if you have multiple series in a chart, you may not want the clutter of viewing all the series, so this app demonstrates how you can extend the legend to toggle series on/off.
If you turn off all series in the app below (with the button at the bottom of the app), you can then toggle on/off the (multiple) series you desire to view by clicking on the legend item for the series you are interested in.
Download a zipfile containing the source to this sample.
Browse the source of this example.
Or continue into the blog entry to see the source:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.charts.events.LegendMouseEvent;
import mx.collections.ArrayCollection;
[Bindable] private var allOn:Boolean=true;
[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 } ]);
private function clickLegend(event:LegendMouseEvent):void{
if(!allOn){
var lineSer:LineSeries = findSeries(event.item.label);
lineSer.visible=(event.item as myLegendItem).selected;
}
}
private function findSeries(lbl:String):LineSeries{
var series:LineSeries = new LineSeries()
for(var i:int=0;i<linechart.series.length;i++){
var tmpSeries:LineSeries = linechart.series[i];
if(tmpSeries.displayName == lbl)
series = tmpSeries;
}
return series;
}
private function toggle():void{
allOn=!allOn;
var a:Array=myLegend.getChildren();
for(var i:int=0;i<a.length;i++){
(a[i] as myLegendItem).selected=false;
}
}
]]>
</mx:Script>
<mx:Panel title="LineChart"
height="100%" width="100%" layout="horizontal">
<mx:LineChart id="linechart" height="100%" width="100%"
paddingLeft="5" paddingRight="5"
showDataTips="true" dataProvider="{expensesAC}">
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="Month"/>
</mx:horizontalAxis>
<mx:series>
<mx:LineSeries yField="Profit" form="curve" displayName="Profitppp"
visible="{allOn}"/>
<mx:LineSeries yField="Expenses" form="curve" displayName="Expenses"
visible="{allOn}"/>
<mx:LineSeries yField="Amount" form="curve" displayName="Amount"
visible="{allOn}"/>
</mx:series>
</mx:LineChart>
<mx:Legend id="myLegend" dataProvider="{linechart}" verticalGap="0" horizontalGap="0" markerWidth="50" markerHeight="4"
itemMouseDown="clickLegend(event)"
legendItemClass="myLegendItem" enabled="{!allOn}" disabledOverlayAlpha="0" >
</mx:Legend>
<mx:ControlBar>
<mx:Button label="Turn all series {(!allOn? ‘on’:'off’)}" click="toggle()"
width="100%"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
import flash.events.MouseEvent;
import mx.charts.LegendItem;
import mx.containers.Canvas;
public class myLegendItem extends LegendItem
{
private var bgElement:Canvas;
private var _selected:Boolean;
public function myLegendItem()
{
super();
addEventListener(MouseEvent.MOUSE_DOWN, handleEvent);
}
override protected function createChildren():void {
super.createChildren();
bgElement = new Canvas();
bgElement.setStyle("backgroundColor", 0x00ff00);
addChildAt(bgElement,0);
}
private function handleEvent(event:MouseEvent):void{
selected=!_selected;
}
public function set selected(value:Boolean):void{
_selected = value;
invalidateDisplayList()
}
public function get selected():Boolean{
return _selected;
}
protected override function updateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth, unscaledHeight);
graphics.clear();
graphics.beginFill(0,0);
graphics.drawRect(0,0,unscaledWidth,unscaledHeight);
graphics.endFill();
name = label;
bgElement.setActualSize(unscaledWidth, unscaledHeight);
if(_selected)
bgElement.visible = true;
else
bgElement.visible = false;
}
}
}
Tweet
11 Comments »

March 10th, 2008 at 4:42 pm
excellent idea!
March 12th, 2008 at 9:07 am
Very cool! Wish I had written it.
matt horn
flex docs
May 6th, 2008 at 5:36 am
[...] Some while ago Kyle posted on his blog an article about interactive legend. [...]
May 6th, 2008 at 5:42 am
hi Kyle,
http://shemesh.wordpress.com/2008/05/06/22/
September 22nd, 2008 at 2:46 pm
Very nice. Thanks for posting the code. Shows me how much there is to learn about getting into the guts of Flex. Cheers.
November 12th, 2008 at 5:59 pm
Actually, perhaps this is not news, but rather than having the findSeries function, which is liable to causing problems in the case that legend items have the same display name, you can change:
var lineSer:LineSeries = findSeries(event.item.label);
to:
var lineSer:LineSeries = event.item.element as Series;
This is a much more direct means, and less prone to errors. Hope this helps.
December 30th, 2008 at 2:43 am
Thanks Kyle for the great example. A slight correction to ryan solution:
var lineSer:LineSeries = event.item.element as LineSeries;
I also found the following (using source insteaad of element) produces same results although I am not sure which one is more safer
var lineSer:LineSeries = event.item.source as LineSeries;
March 6th, 2009 at 8:14 am
Great example, but your code doesn’t work, when you click at a legend symbol. The following code solved this issue:
private function clickLegend(event:MouseEvent):void{
for each(var legendItem:CustomLegendItem in legend.getChildren()){
var series:Series = findSeries(legendItem.label);
series.visible = legendItem.selected;
}
}
September 21st, 2009 at 1:57 pm
I am trying to add a checkbox to the Legend.
And instead of adding a canvas and making the background green, I want to show the checkbox selected.
My problem is when try to add a child (checkbox) in the createChildren() method the checkbox is placed above the legend Marker.
I want all 3 of them (check box, legend Marker (colour box) and the Name ) to be in a row..
Can anyone sugget a way to achieve this
September 21st, 2009 at 3:11 pm
I found a solution to my problem. You have to manually arrange all child components, by setting thier x,y co-ordinates specifically.
Please let me know if there is a better solution.
October 5th, 2009 at 11:55 pm
Thank you so much for the post, Kyle. My project needs just the same thing.
I modified your code a little bit to use in a separate AS file for any chart type:
private function clickLegend(event:LegendMouseEvent, chart:ChartBase):void {
var ser:Series = findSeries(event.item.label, chart);
if(ser != null) {
ser.visible = (event.item as myLegendItem).selected;
}
}
private function findSeries(lbl:String, chart:ChartBase):Series {
for(var i:int = 0; i < chart.series.length;i++) {
if(chart.series[i].displayName == lbl) {
return chart.series[i];
}
}
return null;
}