Flex includes the ability to add graphical elements to your chart controls such as lines, boxes, and elipses. Graphical children of chart controls can also include any control that is a subclass of UIComponent, including list boxes, combo boxes, labels, and even other chart controls.
When working with chart controls, Flex converts x and y coordinates into data coordinates. Data coordinates define locations relative to the data in the chart's underlying data provider. This lets you position graphical elements relative to the location of data points in the chart without having to first convert their positions to x and y coordinates. For example, you can draw a line from the top of a column in a ColumnChart to the the top of another column, showing a trend line.
To add data graphics to a chart control, you use the mx.charts.chartClasses.CartesianDataCanvas (for Cartesian charts) class or the mx.charts.chartClasses.PolarDataCanvas (for polar charts) classes. You can either attach visual controls to the canvas or you can draw on the canvas by using drawing methods.
You attach visual controls to the data canvases in the same way that you programmatically add controls to an application: you add them as child controls. In this case, the method you call to add children is addDataChild(), which lets you add any DisplayObject instance to the canvas. This method lets you take advantage of the data coordinates that the canvas uses. As with most containers, you can also use the addChild() and addChildAt() methods. With these methods, you can then adjust the location of the DisplayObject to data coordinates by using the canvas' updateDataChild() method.
To draw on the data canvases, you use drawing methods that are similar to the methods of the flash.display.Graphics class. The canvases define a set of methods that you can use to create vector shapes such as circles, squares, lines, and other shapes. These methods include the line-drawing methods such as lineTo(), moveTo(), and curveTo(), as well as the fill methods such as beginFill(), beginGradientFill(), beginBitmapFill(), and endFill(). Convenience methods such as drawRect(), drawRoundedRect(), drawEllipse(), and drawCircle() are also available on the data canvases.
All drawn graphics such as lines and shapes remain visible on the data canvas until you call the canvas' clear() method. To remove child objects, you can use the removeChild() and removeChildAt() methods.
A canvas can either be in the foreground (in front of the data points) or in the background (behind the data points). To add a canvas to the foreground, you add it to the chart's annotationElement Array. To add a canvas to the background, you add it to the chart's backgroundElements Array.
The data canvases have the following limitations:
The following example adds a CartesianDataCanvas as an annotation element to the ColumnChart control. It uses the graphics methods to draw a line between the columns that you select.
<?xml version="1.0"?>
<!-- charts/DrawLineBetweenSelectedItems.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
import mx.charts.series.items.ColumnSeriesItem;
import mx.charts.ChartItem;
[Bindable]
public var profits:ArrayCollection = new ArrayCollection([
{Month:"Jan", Profit:1300},
{Month:"Feb", Profit:750},
{Month:"Mar", Profit:1100},
{Month:"Apr", Profit:1000},
{Month:"May", Profit:980},
{Month:"Jun", Profit:1500},
{Month:"Jul", Profit:2060},
{Month:"Aug", Profit:1700},
{Month:"Sep", Profit:1690},
{Month:"Oct", Profit:2200},
{Month:"Nov", Profit:2550},
{Month:"Dec", Profit:3000}
]);
private function connectTwoPoints(month1:String,
value1:Number,
month2:String,
value2:Number
):void {
canvas.clear();
canvas.lineStyle(4,
0xCCCCCC,
.75,
true,
LineScaleMode.NORMAL,
CapsStyle.ROUND,
JointStyle.MITER,
2
);
canvas.moveTo(month1, value1);
canvas.lineTo(month2, value2);
l1.text = "Month: " + month1;
l2.text = "Profit: " + value1;
l3.text = "Month: " + month2;
l4.text = "Profit: " + value2;
chartHasLine = true;
}
private var s1:String = new String();
private var s2:String = new String();
private var v1:Number = new Number();
private var v2:Number = new Number();
// Set this to true initially so that the chart does not
// draw a line when the first item is clicked.
private var chartHasLine:Boolean = true;
private function handleChange(event:Event):void {
var sci:ColumnSeriesItem =
ColumnSeriesItem(myChart.selectedChartItem);
if (chartHasLine) {
canvas.clear();
s1 = sci.item.Month;
v1 = sci.item.Profit;
chartHasLine = false;
} else {
s2 = sci.item.Month;
v2 = sci.item.Profit;
connectTwoPoints(s1, v1, s2, v2);
}
}
]]></mx:Script>
<mx:Panel title="Column Chart">
<mx:ColumnChart id="myChart"
showDataTips="true"
dataProvider="{profits}"
selectionMode="single"
change="handleChange(event)"
>
<mx:annotationElements>
<mx:CartesianDataCanvas id="canvas" includeInRanges="true"/>
</mx:annotationElements>
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{profits}"
categoryField="Month"
/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries
id="series1"
xField="Month"
yField="Profit"
displayName="Profit"
selectable="true"
/>
</mx:series>
</mx:ColumnChart>
<mx:Legend dataProvider="{myChart}"/>
<mx:HBox>
<mx:Button id="b1"
label="Connect Two Points"
click="connectTwoPoints('Jan', 1300, 'Dec', 3000);"
/>
<mx:Button id="b2"
click="canvas.clear()"
label="Clear Line"
/>
</mx:HBox>
<mx:HBox>
<mx:VBox>
<mx:Label text="First Item"/>
<mx:Label id="l1"/>
<mx:Label id="l2"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Second Item"/>
<mx:Label id="l3"/>
<mx:Label id="l4"/>
</mx:VBox>
</mx:HBox>
</mx:Panel>
</mx:Application>
The executing SWF file for the previous example is shown below:
The following example uses the addDataChild() method to add children to the data canvas. It adds labels to each of the columns that you select in the ColumnChart control.
<?xml version="1.0"?>
<!-- charts/AddLabelsWithLines.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
import mx.charts.series.items.ColumnSeriesItem;
import mx.charts.ChartItem;
[Bindable]
public var profits:ArrayCollection = new ArrayCollection([
{Month:"Jan", Profit:1300},
{Month:"Feb", Profit:750},
{Month:"Mar", Profit:1100},
{Month:"Apr", Profit:1000},
{Month:"May", Profit:980},
{Month:"Jun", Profit:1500},
{Month:"Jul", Profit:2060},
{Month:"Aug", Profit:1700},
{Month:"Sep", Profit:1690},
{Month:"Oct", Profit:2200},
{Month:"Nov", Profit:2550},
{Month:"Dec", Profit:3000}
]);
private function connectTwoPoints(month1:String,
value1:Number,
month2:String,
value2:Number
):void {
canvas.clear();
canvas.lineStyle(4,
0xCCCCCC,
.75,
true,
LineScaleMode.NORMAL,
CapsStyle.ROUND,
JointStyle.MITER,
2
);
canvas.moveTo(month1, value1);
canvas.lineTo(month2, value2);
l1.text = "Month: " + month1;
l2.text = "Profit: " + value1;
l3.text = "Month: " + month2;
l4.text = "Profit: " + value2;
chartHasLine = true;
}
private var s1:String = new String();
private var s2:String = new String();
private var v1:Number = new Number();
private var v2:Number = new Number();
// Set this to true initially so that the chart doesn't
// draw a line when the first item is clicked.
private var chartHasLine:Boolean = true;
private function handleChange(event:Event):void {
var sci:ColumnSeriesItem =
ColumnSeriesItem(myChart.selectedChartItem);
if (chartHasLine) {
canvas.clear();
s1 = sci.item.Month;
v1 = sci.item.Profit;
addLabelsToColumn(s1,v1);
chartHasLine = false;
} else {
s2 = sci.item.Month;
v2 = sci.item.Profit;
addLabelsToColumn(s2,v2);
connectTwoPoints(s1, v1, s2, v2);
}
}
[Bindable]
public var columnLabel:Label;
private function addLabelsToColumn(s:String, n:Number):void {
columnLabel = new Label();
columnLabel.setStyle("fontWeight", "bold");
columnLabel.setStyle("color", "0x660000");
columnLabel.text = s + ": " + "$" + n;
// This adds any DisplayObject as child to current canvas.
canvas.addDataChild(columnLabel, s, n);
}
]]></mx:Script>
<mx:Panel title="Column Chart">
<mx:ColumnChart id="myChart"
dataProvider="{profits}"
selectionMode="single"
change="handleChange(event)"
>
<mx:annotationElements>
<mx:CartesianDataCanvas id="canvas" includeInRanges="true"/>
</mx:annotationElements>
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{profits}"
categoryField="Month"
/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries
id="series1"
xField="Month"
yField="Profit"
displayName="Profit"
selectable="true"
/>
</mx:series>
</mx:ColumnChart>
<mx:Legend dataProvider="{myChart}"/>
<mx:Button id="b1"
label="Connect Two Points"
click="connectTwoPoints('Jan', 1300, 'Dec', 3000);"/>
<mx:HBox>
<mx:VBox>
<mx:Label text="First Item"/>
<mx:Label id="l1"/>
<mx:Label id="l2"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Second Item"/>
<mx:Label id="l3"/>
<mx:Label id="l4"/>
</mx:VBox>
</mx:HBox>
</mx:Panel>
</mx:Application>
The executing SWF file for the previous example is shown below:
You can access an array of the data children by using the dataChildren property of the canvas. This property is an Array of the child objects on the canvas.