Events and effects in charts

You can use Flex charting controls to make interesting and engaging charts. Important factors to consider are how users' interactions with your applications trigger effects and events.

Handling user interactions with charts

Chart controls accept many kinds of user interactions, from moving the mouse over a data point to clicking or double-clicking on that data point. You can create an event handler for each of these interactions and use the Event object in that handler to access the data related to that interaction. For example, if a user clicks on a column in a ColumnChart control, you can access that column's data in the click event handler of that chart.

The chart controls support the mouse events that are inherited from the UIComponent class: mouseMove, mouseOver, mouseUp, mouseDown, and mouseOut. These events are of type MouseEvent. In addition, the base class for all chart controls, ChartBase, adds support for the the ChartEvent and ChartItemEvent events.

The ChartItemEvent class defines events that are specific to the chart components, such as when the user clicks a data item in a chart. The ChartEvent class defines events that occur when the user clicks or double-clicks on a chart control, but not on a chart item in that chart control.

The following table describes the ChartEvent event types:

Chart event type

Description

chartClick

Broadcast when the user clicks the mouse button while the mouse pointer is over a chart but not on a chart item.

chartDoubleClick

Broadcast when the user double-clicks the mouse button while the mouse pointer is over a chart but not on a chart item.

The following table describes the ChartItemEvent event types:

Chart data event type

Description

change

Dispatched when the selected item or items changes in the chart.

itemClick

Broadcast when the user clicks the mouse button while over a data point.

itemDoubleClick

Broadcast when the user double-clicks the mouse button while over a data point.

itemMouseDown

Broadcast when the mouse button is down while over a data point.

itemMouseMove

Broadcast when the user moves the mouse pointer while over a data point.

itemMouseUp

Broadcast when the user releases the mouse button while over a data point.

itemRollOut

Broadcast when the closest data point under the mouse pointer changes.

itemRollOver

Broadcast when the user moves the mouse pointer over a new data point.

In addition to the ChartEvent and ChartItemEvent classes, there is also the LegendMouseEvent. This class defines events that are broadcast when the user clicks on legend items or mouses over them.

About chart events

Chart events are triggered when the user clicks or double-clicks the mouse button while the mouse pointer is over a chart control, but not over a chart item in that chart control. These events are dispatched only if the hit data set is empty.

Chart events are of type ChartEvent. Because ChartEvent events are part of the charts package, and not part of the events package, you must import the appropriate classes in the mx.charts.events package to use a ChartEvent event.

The following example logs a ChartEvent when you click or double-click the chart control, but only if you are not over a chart item or within it's range, as determined by its mouseSensitivity property.

<?xml version="1.0"?> 
<!-- charts/BasicChartEvent.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600" width="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
    
<fx:Script><![CDATA[ 
 
 
import mx.charts.events.ChartEvent; 
 
 
 
private function chartEventHandler(event:ChartEvent):void { 
 
 
 
/* 
 
 
 
 * The ChartEvent will only be dispatched if the mouse is _not_ over a 
 * chart item or if it is outside of the range defined by the 
 * mouseSensitivity property. 
 
 
 
*/ 
 
 
 
ta1.text += "Event of type " + event.type + " was triggered.\n";        
} 
 
]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <fx:Style> 
    @namespace s "library://ns.adobe.com/flex/spark"; 
    
    s|Panel { 
        paddingLeft:5; 
        paddingRight:5; 
        paddingTop:5; 
        paddingBottom:5; 
    } 
  </fx:Style> 
  
  <s:Panel title="Chart click events"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <s:HGroup> 
         <mx:PlotChart id="myChart" 
            dataProvider="{srv.lastResult.data.result}" 
            showDataTips="true" 
            mouseSensitivity="10" 
            doubleClickEnabled="true" 
            chartClick="chartEventHandler(event)" 
            chartDoubleClick="chartEventHandler(event)"> 
            <mx:series> 
               <mx:PlotSeries 
                    xField="expenses" 
                    yField="profit" 
                    displayName="Plot 1"/> 
               <mx:PlotSeries 
                    xField="amount" 
                    yField="expenses" 
                    displayName="Plot 2"/> 
               <mx:PlotSeries 
                    xField="profit" 
                    yField="amount" 
                    displayName="Plot 3"/> 
            </mx:series> 
         </mx:PlotChart> 
         <s:TextArea id="ta1" width="150" height="300"/> 
       </s:HGroup> 
     <mx:Legend dataProvider="{myChart}"/> 
  </s:Panel> 
 
</s:Application>

About chart data events

Chart data events are triggered only when the user moves the mouse pointer over a data point, whereas UIComponent events are triggered by any mouse interaction with a control.

The chart data events are of type ChartItemEvent. Because ChartItemEvent events are part of the charts package, and not part of the events package, you must import the appropriate classes in the mx.charts.events package to use a ChartItemEvent event.

The following example opens an alert when the user clicks a data point (a column) in the chart:

<?xml version="1.0"?> 
<!-- charts/DataPointAlert.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
      <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
      <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
      <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
     import mx.controls.Alert; 
     import mx.charts.events.ChartItemEvent; 
 
     public function myHandler(e:ChartItemEvent):void { 
        Alert.show("Chart data was clicked"); 
     } 
  ]]></fx:Script> 
  
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Clickable Column Chart"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" 
        itemClick="myHandler(event)" 
        dataProvider="{srv.lastResult.data.result}"> 
        <mx:horizontalAxis> 
           <mx:CategoryAxis categoryField="month"/> 
        </mx:horizontalAxis> 
        <mx:series> 
           <mx:ColumnSeries yField="expenses" displayName="Expenses"/> 
        </mx:series> 
     </mx:ColumnChart> 
  </s:Panel> 
</s:Application>

Using the HitData object

Flex dispatches a ChartItemEvent object for each chart data event such as when you click on an item in the series. In addition to the standard target and type properties that all Event objects have, Flex adds the hitData property to the ChartItemEvent object. This property is an object instance of the HitData class. The hitData property contains information about the data point that is closest to the mouse pointer at the time of the mouse event.

The item property of the HitData object refers to the data point. You can use that property to access its value by its name, as the previous example shows. The HitData x and y properties refer to the screen coordinates of the data point.

Only data points within the radius determined by the mouseSensitivity property can trigger an event on that data point. For more information, see Changing mouse sensitivity.

The following example uses the itemDoubleClick event handler to display HitData information for data points in a column chart when the user clicks on a column. Because each column in the ColumnChart control is associated with a single data point value, clicking the mouse anywhere in a column displays the same information for the x and y coordinates.

<?xml version="1.0"?> 
<!-- charts/HitDataOnClick.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();init()" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
     import mx.charts.events.ChartItemEvent; 
 
     // Define the event handler. 
     public function myListener(e:ChartItemEvent):void { 
        ti1.text = e.hitData.item.expenses; 
        ti2.text = String(e.hitData.x) + ", " + String(e.hitData.y); 
     } 
 
     // Define event listeners when the application initializes. 
     public function init():void { 
        myChart.addEventListener(ChartItemEvent.ITEM_DOUBLE_CLICK, myListener); 
     } 
  ]]></fx:Script> 
  
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Accessing HitData Object in Event Handlers"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        doubleClickEnabled="true" 
        showDataTips="true"> 
        <mx:horizontalAxis> 
           <mx:CategoryAxis categoryField="month"/> 
        </mx:horizontalAxis> 
        <mx:series> 
           <mx:ColumnSeries yField="expenses"/> 
        </mx:series> 
     </mx:ColumnChart> 
     <s:Form width="100%"> 
        <!--Define a form to display the event information.--> 
        <s:FormItem label="Expenses:"> 
           <s:TextInput id="ti1" width="100%"/> 
        </s:FormItem> 
        <s:FormItem label="x/y:"> 
           <s:TextInput id="ti2" width="100%"/> 
        </s:FormItem> 
     </s:Form> 
  </s:Panel> 
</s:Application>

Getting chart elements

The HitData object accesses the chart's ChartItem objects. These objects represent data points on the screen. In addition to providing access to the data of data points, ChartItem objects provide information about the size and position of graphical elements that make up the chart. For example, you can get the x and y positions of columns in a ColumnChart.

The following example uses a semitransparent Canvas container to highlight the data point that the user clicks on with the mouse. The application also accesses the ChartItem object to get the current value to display in a ToolTip on that Canvas:

<?xml version="1.0"?> 
<!-- charts/ChartItemObjectAccess.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();init()" 
    height="600"> 
 
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
  <fx:Style> 
     @namespace mx "library://ns.adobe.com/flex/mx"; 
     
     mx|ToolTip { 
        fontSize:24; 
     } 
     mx|ColumnChart { 
        gutterLeft: 54; 
     } 
  </fx:Style> 
 
  <fx:Script><![CDATA[ 
     import mx.core.IFlexDisplayObject; 
     import mx.charts.events.ChartItemEvent; 
     import mx.charts.series.items.ColumnSeriesItem; 
     import mx.charts.series.ColumnSeries; 
     import mx.effects.Move; 
     import mx.charts.HitData; 
 
     public var m:mx.effects.Move; 
     public var hitData:mx.charts.HitData; 
     public var chartItem:ColumnSeriesItem; 
     public var renderer:IFlexDisplayObject; 
     public var series:ColumnSeries; 
 
     public var chartItemPoint:Point; 
     public var highlightBoxPoint:Point; 
     public var leftAdjust:Number; 
 
     private function init():void { 
        m = new Move(highlightBox); 
 
        /* This is used to adjust the x location of 
           highlightBox to account for the left gutter width. */ 
        leftAdjust = myChart.getStyle("gutterLeft") - 14; 
        trace("leftAdjust: " + leftAdjust); 
     } 
 
     private function overData(event:ChartItemEvent):void { 
 
        hitData = event.hitData; 
        chartItem = ColumnSeriesItem(hitData.chartItem); 
        renderer = chartItem.itemRenderer; 
        series = ColumnSeries(hitData.element); 
 
        /* Add 10 pixels to give it horizontal overlap. */ 
        highlightBox.width = renderer.width * 2 + 10; 
 
        /* Add 20 pixels to give it vertical overlap. */ 
        highlightBox.height = renderer.height + 20; 
 
        highlightBoxPoint = new Point(highlightBox.x, 
            highlightBox.y); 
 
        /* Convert the ChartItem's pixel values from local 
           (relative to the component) to global (relative 
           to the stage). This enables you to place the Canvas 
           container using the x and y values of the stage. */ 
        chartItemPoint = myChart.localToGlobal(new Point(chartItem.x, chartItem.y)); 
 
        /* Define the parameters of the move effect and play the effect. */ 
        m.xTo = chartItemPoint.x + leftAdjust; 
        m.yTo = chartItemPoint.y; 
        m.duration = 500; 
        m.play(); 
 
        highlightBox.toolTip = "$" + chartItem.yValue.toString(); 
     } 
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel id="p1" title="Highlighted Columns"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        itemClick="overData(event)" 
        mouseSensitivity="0"> 
        <mx:horizontalAxis> 
           <mx:CategoryAxis categoryField="month"/> 
        </mx:horizontalAxis> 
        <mx:series> 
           <mx:ColumnSeries 
                displayName="Expenses" 
                yField="expenses"/> 
           <mx:ColumnSeries 
                displayName="Profit"  
                yField="profit"/> 
        </mx:series> 
     </mx:ColumnChart> 
     <mx:Legend dataProvider="{myChart}"/> 
  </s:Panel> 
 
  <!-- Define the canvas control that will be used as a highlight. --> 
  <mx:Canvas id="highlightBox" 
        y="0" x="0" 
        backgroundColor="0xFFFF00" 
        alpha=".5"/> 
</s:Application>

For information about changing the appearance of ChartItem objects, see Skinning ChartItem objects.

Getting data with coordinates

When you create charts, you can use the coordinates on the screen to get the nearest data point or data points, or conversely, to pass the data point and get the coordinates.

The findDataPoints() and localToData() methods take coordinates and return data. The findDataPoints() method returns a HitData object. The localToData() method returns an Array of the data.

You can also pass the data itself and get the coordinates with the dataToLocal() method.

Using the findDataPoints() method

You can use the chart control's findDataPoints() method to get an Array of HitData objects by passing in x and y coordinates. If the coordinates do not correspond to the location of a data point, the findDataPoints() method returns null. Otherwise, the findDataPoints() method returns an Array of HitData objects.

The findDataPoints() method has the following signature:

 findDataPoints(x:Number, y:Number):Array

The following example creates a PlotChart control and records the location of the mouse pointer as the user moves the mouse over the chart. It uses the findDataPoints() method to get an Array of HitData objects, and then displays some of the first object's properties.

<?xml version="1.0"?> 
<!-- charts/FindDataPoints.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
     import mx.charts.HitData; 
 
     public function handleMouseMove(e:MouseEvent):void { 
        // Use coordinates to get HitData object of 
        // current data point. 
        var hda:Array = 
            chart.findDataPoints(e.currentTarget.mouseX, 
            e.currentTarget.mouseY); 
        if (hda[0]) { 
           ta.text = "Found data point " + 
                hda[0].chartItem.index + " (x/y):" + 
                Math.round(hda[0].x) + "," + 
                Math.round(hda[0].y) + "\n"; 
           ta.text += "Expenses:" + hda[0].item.expenses; 
        } else { 
           ta.text = "No data point found  (x/y):" + 
                Math.round(e.currentTarget.mouseX) + 
                "/" + Math.round(e.currentTarget.mouseY); 
        } 
     } 
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Plot Chart"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:PlotChart id="chart" 
        mouseMove="handleMouseMove(event)" 
        dataProvider="{srv.lastResult.data.result}" 
        showDataTips="true" 
        mouseSensitivity="5"> 
        <mx:series> 
           <mx:PlotSeries 
                xField="profit" 
                yField="expenses"/> 
        </mx:series> 
     </mx:PlotChart> 
  </s:Panel> 
  <s:TextArea id="ta" width="300" height="50"/> 
</s:Application>
Using the localToData() method

The localToData() method takes a Point object that represents the x and y coordinates you want to get the data for and returns an Array of data values, regardless of whether any data items are at or near that point. You call this method on the series.

The following example creates a Point object from the mouse pointer's location on the screen and displays the data values associated with that point:

<?xml version="1.0"?> 
<!-- charts/LocalToData.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
     
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/stocks-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/stocks.aspx -->  
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
     import mx.collections.ArrayCollection; 
 
     public var p:Point; 
 
     private function updateDetails(e:MouseEvent):void { 
        p = new Point(myChart.mouseX, myChart.mouseY); 
        mpos.text = "(" + p.x + "," + p.y + ")"; 
 
        var d:Array = series1.localToData(p); 
        var da:Date = new Date(d[0]);     
        
        // Round y coordinate value on column to two places. 
        var v:Number = int((d[1])*100)/100; 
        
        dval.text = "(" + daxis.formatForScreen(da) + ", " + v + ")";        
        p = series1.dataToLocal(d[0], d[1]); 
        dpos.text ="(" + Math.floor(p.x) + ", " + Math.floor(p.y) + ")"; 
     } 
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Column Chart"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        mouseMove="updateDetails(event)" 
        showDataTips="true"> 
        <mx:horizontalAxis> 
           <mx:DateTimeAxis id="daxis" dataUnits="days"/> 
        </mx:horizontalAxis> 
        <mx:series> 
           <mx:ColumnSeries id="series1" 
                xField="date" 
                yField="close" 
                displayName="Close"/> 
        </mx:series> 
     </mx:ColumnChart> 
     <mx:Legend dataProvider="{myChart}"/> 
  </s:Panel> 
 
  <s:Form width="300"> 
     <s:FormItem label="Mouse Position:"> 
        <s:Label id="mpos"/> 
     </s:FormItem> 
     <s:FormItem label="Data Position:"> 
        <s:Label id="dpos"/> 
     </s:FormItem> 
     <s:FormItem label="Data:"> 
        <s:Label id="dval"/> 
     </s:FormItem> 
  </s:Form> 
 
</s:Application>

The chart type determines how coordinates are mapped, and how many values are returned in the Array. The values returned are typically numeric values.

In a chart that is based on the CartesianChart class (for example, a BarChart or ColumnChart control), the first item in the returned Array is the value of the x-coordinate along the horizontal axis, and the second item is the value of the y-coordinate along the vertical axis.

In a chart based on the PolarChart class (such as PieChart), the returned Array maps the coordinates to a set of polar coordinates—an angle around the center of the chart, and a distance from the center. Those values are mapped to data values that use the first (angular) and second (radial) axes of the chart.

Using the dataToLocal() method

The dataToLocal() method converts a set of values to x and y coordinates on the screen. The values you give the method are in the "data space" of the chart; this method converts these values to coordinates. The data space is the collection of all possible combinations of data values that a chart can represent.

The number and meaning of arguments passed to the dataToLocal() method depend on the chart type. For CartesianChart controls, such as the BarChart and ColumnChart controls, the first value is used to map along the x axis, and the second value is used to map along the y axis.

For PolarChart controls, such as the PieChart control, the first value maps to the angle around the center of the chart, and the second value maps to the distance from the center of the chart along the radius.

The coordinates returned are based on 0,0 being the upper-left corner of the chart. For a ColumnChart control, for example, the height of the column is inversely related to the x coordinate that is returned.

Changing mouse sensitivity

You use the mouseSensitivity property of the chart control to determine when the mouse pointer is considered to be over a data point; for example:

 <mx:ColumnChart id="chart" dataProvider="{dataSet}" mouseSensitivity="30">

The current data point is the nearest data point to the mouse pointer that is less than or equal to the number of pixels that the mouseSensitivity property specifies.

The default value of the mouseSensitivity property is 3 pixels. If the mouse pointer is 4 or more pixels away from a data point, Flex does not trigger a ChartItemEvent (of types such as itemRollOver or itemClick). Flex still responds to events such as mouseOver and click by generating a ChartEvent object of type chartClick or chartDoubleClick.

The following example initially sets the mouseSensitivity property to 20, but lets the user change this value with the HSlider control:

<?xml version="1.0"?> 
<!-- charts/MouseSensitivity.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Mouse Sensitivity"> 
     <s:layout> 
         <s:VerticalLayout/> 
     </s:layout> 
     <mx:PlotChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        showDataTips="true" 
        mouseSensitivity="{mySlider.value}"> 
        <mx:series> 
           <mx:PlotSeries 
                xField="expenses" 
                yField="profit" 
                displayName="Plot 1"/> 
           <mx:PlotSeries 
                xField="amount" 
                yField="expenses" 
                displayName="Plot 2"/> 
           <mx:PlotSeries 
                xField="profit" 
                yField="amount" 
                displayName="Plot 3"/> 
        </mx:series> 
     </mx:PlotChart> 
 
     <s:HGroup> 
         <mx:Legend dataProvider="{myChart}"/> 
         <s:VGroup> 
             <s:Label text="Mouse Sensitivity:"/> 
             <mx:HSlider id="mySlider" 
                minimum="0" 
                maximum="300" 
                value="20" 
                dataTipPlacement="top" 
                tickColor="black" 
                snapInterval="1" 
                tickInterval="20" 
                labels="['0','300']" 
                allowTrackClick="true" 
                liveDragging="true"/> 
        </s:VGroup> 
      </s:HGroup> 
  </s:Panel> 
</s:Application>

You can use the mouseSensitivity property to increase the area that triggers DataTip events or emits chart-related events. If the mouse pointer is within the range of multiple data points, Flex chooses the closest data point. For DataTip events, if you have multiple DataTip controls enabled, Flex displays all DataTip controls within range. For more information, see Showing multiple DataTip objects.

Disabling interactivity

You can make the series in a chart ignore all mouse events by setting the interactive property to false for that series. The default value is true. This lets you disable mouse interactions for one series while allowing it for another.

The following example disables interactivity for events on the first and third PlotSeries objects:

<?xml version="1.0"?> 
<!-- charts/DisableInteractivity.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Disable Interactivity"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:PlotChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        showDataTips="true"> 
        <mx:series> 
           <mx:PlotSeries 
                xField="expenses" 
                yField="profit" 
                displayName="Plot 1" 
                interactive="false"/> 
           <mx:PlotSeries 
                xField="amount" 
                yField="expenses" 
                displayName="Plot 2"/> 
           <mx:PlotSeries 
                xField="profit" 
                yField="amount" 
                displayName="Plot 3" 
                interactive="false"/> 
        </mx:series> 
     </mx:PlotChart> 
     <mx:Legend dataProvider="{myChart}"/> 
  </s:Panel> 
</s:Application>

Disabling the series interactivity has the following results:

  • The series does not show DataTip controls.

  • The series does not generate a hitData structure on any chart data event.

  • The series does not return a hitData structure when you call the findDataPoints() method on the chart.

Using effects with charts

Chart controls support the standard Flex effects such as Zoom and Fade. You can use these effects to make the entire chart control zoom in or fade out in your applications. In addition, chart data series support the following effect classes that apply to the data in the chart:

These effects zoom or slide the chart items inside the chart control. These classes are in the mx.charts.effects.effects.* package. The base class for chart effects is SeriesEffect.

All chart controls and series support the standard Flex triggers and effects that are inherited from UIComponent. These triggers include focusInEffect, focusOutEffect, moveEffect, showEffect, and hideEffect. The charting controls also include the showDataEffect and hideDataEffect triggers.

For information on creating complex effects, see Introduction to effects.

Using standard effect triggers

Chart controls support standard effects triggers, such as showEffect and hideEffect.

The following example defines a set of wipe effects that Flex executes when the user toggles the chart's visibility by using the Button control. Also, Flex fades in the chart when it is created.

<?xml version="1.0"?> 
<!-- charts/StandardEffectTriggers.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
          <!-- Define the effects --> 
          <mx:Parallel id="showEffects"> 
             <mx:WipeRight duration="2000"/> 
             <mx:Fade alphaFrom="0" alphaTo="1" duration="4000"/> 
          </mx:Parallel> 
 
          <mx:Parallel id="hideEffects"> 
             <mx:Fade alphaFrom="1" alphaTo="0" duration="2500"/> 
             <mx:WipeLeft duration="3000"/> 
          </mx:Parallel> 
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Area Chart with Effects"> 
     <s:layout> 
         <s:HorizontalLayout/> 
     </s:layout> 
     <mx:AreaChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        creationCompleteEffect="showEffects" 
        hideEffect="hideEffects" 
        showEffect="showEffects"> 
        <mx:horizontalAxis> 
           <mx:CategoryAxis categoryField="month"/> 
        </mx:horizontalAxis> 
        <mx:series> 
           <mx:AreaSeries yField="profit" displayName="Profit"/> 
           <mx:AreaSeries yField="expenses" displayName="Expenses"/> 
        </mx:series> 
     </mx:AreaChart> 
     <mx:Legend dataProvider="{myChart}"/> 
  </s:Panel> 
 
  <s:Button label="Toggle visibility" 
    click="myChart.visible=!myChart.visible"/> 
</s:Application>

A negative aspect of using standard effect triggers with charts is that the effects are applied to the entire chart control, and not just the data in the chart control. The result is that an effect such as Fade causes the chart's axes, gridlines, labels, and other chart elements, in addition to the chart's data, to fade in or out during the effect. To solve this, you use special charting effect triggers (see Using charting effect triggers).

Using charting effect triggers

Charts have two unique effect triggers: hideDataEffect and showDataEffect. You set these triggers on the data series for the chart. Whenever the data for a chart changes, Flex executes these triggers in succession on the chart's data. The chart control's other elements, such as gridlines, axis lines, and labels are not affected by the effect.

The hideDataEffect trigger defines the effect that Flex uses as it hides the current data from view. The showDataEffect trigger defines the effect that Flex uses as it moves the current data into its final position on the screen.

Because Flex triggers the effects associated with hideDataEffect and showDataEffect when the data changes, there is "old" data and "new" data. The effect associated with the hideDataEffect trigger is the "old" data that will be replaced by the new data.

The order of events is:

  1. Flex first uses the hideDataEffect trigger to invoke the effect set for each element of a chart that is about to change. These triggers execute at the same time.

  2. Flex then updates the chart with its new data. Any elements (including the grid lines and axes) that do not have effects associated with them are updated immediately with their new values.

  3. Flex then invokes the effect set with the showDataEffect trigger for each element associated with them. Flex animates the new data in the chart.

Charting effects with data series

Three effects classes are unique to charting: SeriesInterpolate, SeriesSlide, and SeriesZoom. You use these effects on a data series to achieve an effect when the data in that series changes. These effects can have a great deal of visual impact. Data series do not support other Flex effects.

The following example shows the SeriesSlide effect making the data slide in and out of a screen when the data changes. You can trigger changes to data in a chart series in many ways. Most commonly, you trigger an effect when the data provider changes for a chart control. In the following example, when the user clicks the button, the chart control's data provider changes, triggering the effect:

<?xml version="1.0"?> 
<!-- charts/BasicSeriesSlideEffect.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv_fred.send();srv_strk.send()" 
    height="600"> 
 
    <fx:Declarations> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/stocks.aspx -->  
         <!-- View source of the following pages to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv_fred" url="http://examplesserver/chart_examples/stocks-xml.aspx?tickerSymbol=FRED"/> 
         <mx:HTTPService id="srv_strk" url="http://examplesserver/chart_examples/stocks-xml.aspx?tickerSymbol=STRK"/> 
 
        <!-- Define chart effects --> 
        <mx:SeriesSlide 
            id="slideIn" 
            duration="1000" 
            direction="up"/> 
        <mx:SeriesSlide 
            id="slideOut" 
            duration="1000" 
            direction="down"/> 
    </fx:Declarations> 
 
    <fx:Script><![CDATA[ 
        public function changeProvider():void { 
            if (series1.displayName == "STRK") { 
                myChart.dataProvider = srv_fred.lastResult.data.result; 
                series1.displayName = "FRED"; 
                lbl.text = "Ticker: " + series1.displayName; 
            } else { 
                myChart.dataProvider = srv_strk.lastResult.data.result; 
                series1.displayName = "STRK"; 
                lbl.text = "Ticker: " + series1.displayName; 
            } 
        } 
    ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel title="Column Chart with Basic Series Slide Effect" 
        height="493"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <s:Label id="lbl" text="Ticker: FRED" 
            width="233.5" height="44" 
            fontSize="24" color="#091D96" 
            top="5" right="10"/> 
        <mx:ColumnChart id="myChart" 
            dataProvider="{srv_fred.lastResult.data.result}"> 
            <mx:horizontalAxis> 
                <mx:DateTimeAxis dataUnits="days"/> 
            </mx:horizontalAxis> 
 
            <mx:verticalAxis> 
                <mx:LinearAxis minimum="0" maximum="100"/> 
            </mx:verticalAxis> 
 
            <mx:series> 
                <mx:ColumnSeries id="series1" 
                    xField="date" yField="close" 
                    displayName="FRED" 
                    showDataEffect="slideIn" 
                    hideDataEffect="slideOut"/> 
            </mx:series> 
        </mx:ColumnChart> 
        <mx:Legend dataProvider="{myChart}" bottom="0"/> 
    </s:Panel> 
    <s:Button id="b1" click="changeProvider()" label="Toggle Ticker"/> 
</s:Application>

This example explicitly defines the minimum and maximum values of the vertical axis. If not, Flex would recalculate these values when the new data provider was applied. The result could be a change in the axis labels during the effect.

Changing a data provider first triggers the hideDataEffect effect on the original data provider, which causes that data provider to "slide out," and then triggers the showDataEffect effect on the new data provider, which causes that data provider to "slide in."

Note: If you set the data provider on the series and not the chart control, you must change it on the series and not the chart control.

Another trigger of data effects is when a new data point is added to a series. The following example triggers the showDataEffect when the user clicks the button and adds a new item to the series' data provider:

<?xml version="1.0"?> 
<!-- charts/AddItemEffect.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
    
     <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/widgets-xml.aspx" result="resultHandler(event)"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/widgets.aspx -->  
        <!-- Define chart effect -->    
        <mx:SeriesSlide id="slideIn" 
            duration="1000" 
            direction="up" 
        />  
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <fx:Script><![CDATA[ 
        import mx.collections.ArrayCollection; 
 
          [Bindable] 
          public var items:ArrayCollection; 
     
        public function addDataItem():void { 
            // Add a randomly generated value to 
            // the data provider. 
            var n:Number = Math.random() * 3000; 
            var o:Object = {item: n}; 
            items.addItem(o); 
        } 
        
        private function resultHandler(e:Event):void { 
          items = ArrayCollection(srv.lastResult.data.result); 
        } 
    ]]></fx:Script> 
 
    <s:Panel title="Column Chart with Series Effect"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:ColumnChart id="myChart" dataProvider="{items}">        
            <mx:series> 
                <mx:ColumnSeries yField="item" 
                    displayName="Quantity" 
                    showDataEffect="slideIn"/> 
            </mx:series> 
        </mx:ColumnChart> 
    </s:Panel> 
    <s:Button id="b1" click="addDataItem()" label="Add Data Item"/> 
</s:Application>

This example casts the results of the HTTPService call to an ArrayCollection, so that you can add new items with the addItem() method. The new items are not added to the data on the server, but just in the local application's results ArrayCollection. For more information about changing charting data at run time, see Changing chart data at run time.

The charting effects have several properties in common that traditional effects do not have. All of these properties are optional. The following table lists the common properties of the charting effects:

Property

Description

duration

The amount of time, in milliseconds, that Flash Player takes to complete the entire effect. This property defines the speed with which the effect executes.

The duration property acts as a minimum duration. The effect can take longer based on the settings of other properties.

The default value is 500.

elementOffset

The amount of time, in milliseconds, that Flash Player delays the effect on each element in the series.

Set the elementOffset property to 0 to make all elements start at the same time and end at the same time.

Set the elementOffset property to an integer such as 30 to stagger the effect on each element by that amount of time. For example, with a slide effect, the first element slides in immediately. The next element begins 30 milliseconds later, and so on. The amount of time for the effect to execute is the same for each element, but the overall duration of the effect is longer.

Set the elementOffset property to a negative value to make the effect display the last element in the series first.

The default value is 20.

minimumElementDuration

The amount of time, in milliseconds, that an individual element should take to complete the effect.

Charts with a variable number of data points in the series cannot reliably create smooth effects with only the duration property.

For example, an effect with a duration of 1000 and elementOffset of 100 takes 900 milliseconds per element to complete if you have two elements in the series. This is because the start of each effect is offset by 100, and each effect finishes in 1000 milliseconds. If there are four elements in the series, each element takes 700 milliseconds to complete (the last effect starts 300 milliseconds after the first and must be completed within 1000 milliseconds). With 10 elements, each element has only 100 milliseconds to complete the effect.

The value of the minimumElementDuration property sets a minimal duration for each element. No element of the series takes less than this amount of time (in milliseconds) to execute the effect. As a result, it is possible for an effect to take longer than a specified duration if you specify at least two of the following three properties: duration, offset, and minimumElementDuration.

The default value is 0, which means that the duration property is used to control the total length of time, in milliseconds, that Flex takes to complete the effect.

offset

The amount of time, in milliseconds, that Flash Player delays the start of the effect. Use this property to stagger effects on multiple series.

The default value is 0.

Using the SeriesSlide effect

The SeriesSlide effect slides a data series into and out of the chart's boundaries. The SeriesSlide effect takes a direction property that defines the location from which the series slides. Valid values of direction are left, right, up, or down.

If you use the SeriesSlide effect with a hideDataEffect trigger, the series slides from the current position on the screen to a position off the screen, in the direction indicated by the direction property. If you use SeriesSlide with a showDataEffect trigger, the series slides from off the screen to a position on the screen, in the indicated direction.

When you use the SeriesSlide effect, the entire data series disappears from the chart when the effect begins. The data then reappears based on the nature of the effect. To keep the data on the screen at all times during the effect, you can use the SeriesInterpolate effect. For more information, see Using the SeriesInterpolate effect.

The following example creates an effect called slideDown. Each element starts its slide 30 milliseconds after the element before it, and takes at least 20 milliseconds to complete its slide. The entire effect takes at least 1 second (1000 milliseconds) to slide the data series down. Flex invokes the effect when it clears old data from the chart and when new data appears.

<?xml version="1.0"?> 
<!-- charts/CustomSeriesSlideEffect.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
     
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/widgets-xml.aspx" result="resultHandler(event)"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/widgets.aspx -->  
          <!-- Define chart effect --> 
          <mx:SeriesSlide duration="1000" 
            direction="down" 
            minimumElementDuration="20" 
            elementOffset="30" 
            id="slideDown" 
          /> 
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
     import mx.collections.ArrayCollection; 
 
     [Bindable] 
     public var items:ArrayCollection; 
 
     public function addDataItem():void { 
        // Add a randomly generated value to the data provider 
        var n:Number = Math.random() * 3000; 
        var o:Object = {item:n}; 
        items.addItem(o); 
     } 
     private function resultHandler(e:Event):void { 
          items = ArrayCollection(srv.lastResult.data.result); 
     } 
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Column Chart with Custom Series Slide Effect"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" dataProvider="{items}"> 
        <mx:series> 
           <mx:ColumnSeries 
            yField="item" 
            displayName="Quantity" 
            showDataEffect="slideDown" 
            hideDataEffect="slideDown"/> 
        </mx:series> 
     </mx:ColumnChart> 
  </s:Panel> 
  <s:Button id="b1" click="addDataItem()" label="Add Data Item"/> 
</s:Application>

Using the SeriesZoom effect

The SeriesZoom effect implodes and explodes chart data into and out of the focal point that you specify. As with the SeriesSlide effect, whether the effect is zooming to or from this point depends on whether it's used with a showDataEffect or hideDataEffect trigger.

The SeriesZoom effect can take several properties that define how the effect acts. The following table describes these properties:

Property

Description

horizontalFocus

verticalFocus

Defines the location of the focal point of the zoom.

You combine the horizontalFocus and verticalFocus properties to define the point from which the data series zooms in and out. For example, set the horizontalFocus property to left and the verticalFocus property to top to have the series data zoom to and from the upper-left corner of either element or the chart (depending on the setting of the relativeTo property).

Valid values of the horizontalFocus property are left, center, right, and undefined.

Valid values of the verticalFocus property are top, center, bottom, and undefined.

If you specify only one of these two properties, the focus is a horizontal or vertical line rather than a point. For example, when you set the horizontalFocus property to left, the element zooms to and from a vertical line along the left edge of its bounding box. Setting the verticalFocus property to center causes the element to zoom to and from a horizontal line along the middle of its bounding box.

The default value for both properties is center.

relativeTo

Controls the bounding box used to calculate the focal point of the zooms. Valid values for relativeTo are series and chart.

Set the relativeTo property to series to zoom each element relative to itself. For example, each column of a ColumnChart zooms from the upper-left of the column.

Set the relativeTo property to chart to zoom each element relative to the chart area. For example, each column zooms from the upper-left of the axes, the center of the axes, and so on.

The following example zooms in the data series from the upper-right corner of the chart. While zooming in, Flex displays the last element in the series first because the elementOffset value is negative.

<?xml version="1.0"?> 
<!-- charts/SeriesZoomEffect.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/widgets-xml.aspx" result="resultHandler(event)"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/widgets.aspx -->  
      <!-- Define chart effects --> 
      <mx:SeriesZoom id="zoomOut" 
         duration="2000" 
         minimumElementDuration="50" 
         elementOffset="50" 
         verticalFocus="top" 
         horizontalFocus="left" 
         relativeTo="chart"/> 
      <mx:SeriesZoom id="zoomIn" 
         duration="2000" 
         minimumElementDuration="50" 
         elementOffset="-50" 
         verticalFocus="top" 
         horizontalFocus="right" 
         relativeTo="chart"/> 
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
     import mx.collections.ArrayCollection; 
 
     [Bindable] 
     public var items:ArrayCollection; 
 
     public function addDataItem():void { 
        // Add a randomly generated value to the data provider. 
        var n:Number = Math.random() * 3000; 
        var o:Object = {item: n}; 
        items.addItem(o); 
     } 
     private function resultHandler(e:Event):void { 
          items = ArrayCollection(srv.lastResult.data.result); 
     } 
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Column Chart with Series Zoom Effect"> 
     <s:layout> 
         <s:VerticalLayout/> 
     </s:layout> 
     <mx:ColumnChart id="myChart" dataProvider="{items}"> 
        <mx:series> 
           <mx:ColumnSeries 
            yField="item" 
            displayName="Quantity" 
            showDataEffect="zoomIn" 
            hideDataEffect="zoomOut"/> 
        </mx:series> 
     </mx:ColumnChart> 
  </s:Panel> 
  <s:Button id="b1" click="addDataItem()" label="Add Data Item"/> 
</s:Application>

When you use the SeriesZoom effect, the entire data series disappears from the chart when the effect begins. The data then reappears based on the nature of the effect. To have the data stay on the screen at all times during the effect, you can use the SeriesInterpolate effect. For more information, see Using the SeriesInterpolate effect.

Using the SeriesInterpolate effect

The SeriesInterpolate effect moves the graphics that represent the existing data in the series to the new points. Instead of clearing the chart and then repopulating it as with SeriesZoom and SeriesSlide, this effect keeps the data on the screen at all times.

You use the SeriesInterpolate effect only with the showDataEffect effect trigger. It has no effect if set with a hideDataEffect trigger.

The following example sets the elementOffset property of the SeriesInterpolate effect to 0. As a result, all elements move to their new locations without disappearing from the screen.

<?xml version="1.0"?> 
<!-- charts/SeriesInterpolateEffect.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/widgets-xml.aspx" result="resultHandler(event)"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/widgets.aspx -->  
      <!-- Define chart effect. -->  
      <mx:SeriesInterpolate id="rearrangeData" 
         duration="1000" 
         minimumElementDuration="200" 
         elementOffset="0" 
      /> 
 
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
     import mx.collections.ArrayCollection; 
 
     [Bindable] 
     public var items:ArrayCollection; 
 
     public function addDataItem():void { 
        // Add a randomly generated value to the data provider. 
        var n:Number = Math.random() * 3000; 
        var o:Object = {item: n}; 
        items.addItem(o); 
     } 
     private function resultHandler(e:Event):void { 
          items = ArrayCollection(srv.lastResult.data.result); 
     } 
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Column Chart with Series Interpolate Effect"> 
     <s:layout> 
         <s:VerticalLayout/> 
     </s:layout> 
     <mx:ColumnChart id="myChart" dataProvider="{items}"> 
        <mx:series> 
           <mx:ColumnSeries 
            yField="item" 
            displayName="Quantity" 
            showDataEffect="rearrangeData" 
           /> 
        </mx:series> 
     </mx:ColumnChart> 
  </s:Panel> 
  <s:Button id="b1" click="addDataItem()" label="Add Data Item"/> 
</s:Application>

Applying chart series effects with ActionScript

You can define effects and apply them to chart series by using ActionScript. One way to apply an effect is the same as the way you apply style properties. You use the setStyle() method and Cascading Style Sheets (CSS) to apply the effect.

For more information on using styles, see Styles and themes.

The following example defines the slideIn effect that plays every time the user adds a data item to the chart control's ColumnSeries. The effect is applied to the series by using the setStyle() method when the application first loads.

<?xml version="1.0"?> 
<!-- charts/ApplyEffectsAsStyles.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="initApp();srv.send();" 
    height="600"> 
    
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/widgets-xml.aspx" result="resultHandler(event)"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/widgets.aspx -->  
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <fx:Script><![CDATA[ 
     import mx.collections.ArrayCollection; 
     import mx.charts.effects.SeriesInterpolate; 
 
     [Bindable] 
     public var items:ArrayCollection; 
 
     public var rearrangeData:SeriesInterpolate; 
 
     public function initApp():void { 
        rearrangeData = new SeriesInterpolate(); 
        rearrangeData.duration = 1000; 
        rearrangeData.minimumElementDuration = 200; 
        rearrangeData.elementOffset = 0; 
 
        // Apply the effect as a style. 
        mySeries.setStyle("showDataEffect", rearrangeData); 
     } 
 
     public function addDataItem():void { 
        // Add a randomly generated value to the data provider. 
        var n:Number = Math.random() * 3000; 
        var o:Object = {item: n}; 
        items.addItem(o); 
     } 
     private function resultHandler(e:Event):void { 
          items = ArrayCollection(srv.lastResult.data.result); 
     } 
  ]]></fx:Script> 
 
  <s:Panel title="Column Chart with Series Interpolate Effect"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" dataProvider="{items}"> 
        <mx:series> 
           <mx:ColumnSeries id="mySeries" 
            yField="item" displayName="Quantity"/> 
        </mx:series> 
     </mx:ColumnChart> 
  </s:Panel> 
 
  <s:Button id="b1" click="addDataItem()" label="Add Data Item"/> 
 
</s:Application>

When you define an effect in ActionScript, you must import the appropriate classes. If you define an effect by using MXML, the compiler imports the class for you.

Instead of applying effects with the setStyle() method, you can apply them with CSS if you predefine them in your MXML application; for example:

<?xml version="1.0"?> 
<!-- charts/ApplyEffectsWithCSS.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();" 
    height="600"> 
    
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/widgets-xml.aspx" result="resultHandler(event)"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/widgets.aspx -->  
        <!-- Define chart effect. --> 
        <mx:SeriesSlide 
            duration="1000" 
            direction="down" 
            minimumElementDuration="20" 
            elementOffset="30" 
            id="slideDown"/> 
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <fx:Style> 
     @namespace mx "library://ns.adobe.com/flex/mx"; 
 
     mx|ColumnSeries { 
        showDataEffect:slideDown; 
        hideDataEffect:slideDown; 
     } 
  </fx:Style> 
 
  <fx:Script><![CDATA[ 
     import mx.collections.ArrayCollection; 
 
     [Bindable] 
     public var items:ArrayCollection; 
 
     public function addDataItem():void { 
        // Add a randomly generated value to the data provider. 
        var n:Number = Math.random() * 3000; 
        var o:Object = {item:n}; 
        items.addItem(o); 
     } 
     private function resultHandler(e:Event):void { 
          items = ArrayCollection(srv.lastResult.data.result); 
     } 
  ]]></fx:Script> 
 
  <s:Panel title="Column Chart with Custom Series Slide Effect"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" dataProvider="{items}"> 
        <mx:series> 
           <mx:ColumnSeries yField="item" displayName="Quantity"/> 
        </mx:series> 
     </mx:ColumnChart> 
  </s:Panel> 
  <s:Button id="b1" click="addDataItem()" label="Add Data Item"/> 
</s:Application>

You can use controls such as a slider to let the user adjust the effect's properties. To bind the properties of an ActionScript object, such as an effect, to a control, you use methods of the BindingUtils class, as the following example shows:

<?xml version="1.0"?> 
<!-- charts/ApplyEffectsWithActionScriptSlider.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="initApp();srv.send();" 
    height="750"> 
    
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/widgets-xml.aspx" result="resultHandler(event)"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/widgets.aspx -->  
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <fx:Script><![CDATA[ 
     import mx.collections.ArrayCollection; 
     import mx.charts.effects.SeriesInterpolate; 
     import mx.binding.utils.BindingUtils; 
 
     public var rearrangeData:SeriesInterpolate; 
     
     [Bindable] 
     public var items:ArrayCollection; 
 
     public function initApp():void { 
        rearrangeData = new SeriesInterpolate(); 
 
        // Bind effect properties to slider controls 
        BindingUtils.bindProperty(rearrangeData, "duration", durationSlider, "value"); 
        BindingUtils.bindProperty(rearrangeData, "minimumElementDuration", 
            minimumElementDurationSlider, "value"); 
        BindingUtils.bindProperty(rearrangeData, "elementOffset", 
            elementOffsetSlider, "value"); 
     } 
 
     public function addDataItem():void { 
        // Add a randomly generated value to the data provider. 
        var n:Number = Math.random() * 3000; 
        var o:Object = {item: n}; 
        items.addItem(o); 
     } 
 
     public function resetSliders():void { 
        durationSlider.value = 1000; 
        minimumElementDurationSlider.value = 200; 
        elementOffsetSlider.value = 0; 
     } 
 
     private function resultHandler(e:Event):void { 
          items = ArrayCollection(srv.lastResult.data.result); 
     } 
  ]]></fx:Script> 
 
  <s:Panel title="Column Chart with Series Interpolate Effect"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" dataProvider="{items}"> 
        <mx:series> 
           <mx:ColumnSeries id="mySeries" 
            yField="item" displayName="Quantity" 
            showDataEffect="rearrangeData"/> 
        </mx:series> 
     </mx:ColumnChart> 
  </s:Panel> 
 
  <s:Button id="b1" click="addDataItem()" label="Add Data Item"/> 
 
  <s:Panel title="Effects"> 
     <s:Form> 
        <s:FormItem label="Duration"> 
           <mx:HSlider id="durationSlider" 
            minimum="1" 
            maximum="10000" 
            value="1000" 
            dataTipPlacement="top" 
            tickColor="black" 
            snapInterval="500" 
            tickInterval="500" 
            labels="['0','10000']" 
            allowTrackClick="true" 
            liveDragging="true"/> 
        </s:FormItem> 
        <s:FormItem label="Minimum Element Duration"> 
           <mx:HSlider id="minimumElementDurationSlider" 
            minimum="0" 
            maximum="1000" 
            value="200" 
            dataTipPlacement="top" 
            tickColor="black" 
            snapInterval="50" 
            tickInterval="50" 
            labels="['0','1000']" 
            allowTrackClick="true" 
            liveDragging="true"/> 
        </s:FormItem> 
        <s:FormItem label="Element Offset"> 
           <mx:HSlider id="elementOffsetSlider" 
            minimum="0" 
            maximum="1000" 
            value="0" 
            dataTipPlacement="top" 
            tickColor="black" 
            snapInterval="50" 
            tickInterval="50" 
            labels="['0','1000']" 
            allowTrackClick="true" 
            liveDragging="true"/> 
        </s:FormItem> 
     </s:Form> 
  </s:Panel> 
 
  <s:Button id="b2" label="Reset Sliders" click="resetSliders()"/> 
 
</s:Application>

For more information about databinding in ActionScript, see Defining data bindings in ActionScript.

Drilling down into data

One common use of charts is to allow the user to drill down into the data. This usually occurs when the user performs some sort of event on the chart such as clicking on a wedge in a PieChart control or clicking on a column in a ColumnChart control. Clicking on a data item reveals a new chart that describes the make-up of that data item.

For example, you might have a ColumnChart control that shows the month-by-month production of widgets. To initially populate this chart, you might make a database call (through a service or some other adapter). If the user then clicks on the January column, the application could display the number of widgets of each color that were produced that month. To get the individual month's widget data, you typically make another database call and pass a parameter to the listening service that describes the specific data you want. You can then use the resulting data provider to render the new view.

Typically, when you drill down into chart data, you create new charts in your application with ActionScript. When creating new charts in ActionScript, you must be sure to create a series, add it to the new chart's series Array, and then call the addElement() method (on Spark containers) or addChild() method (on MX containers) to add the new chart to the display list. For more information, see Creating charts in ActionScript.

One way to provide drill-down functionality is to make calls that are external to the application to get the drill-down data. You typically do this by using the chart's itemClick event listener, which gives you access to the HitData object. The HitData object lets you examine what data was underneath the mouse when the event was triggered. This capability lets you perform actions on specific chart data. For more information, see Using the HitData object.

You can also use a simple Event object to get a reference to the series that was clicked. The following example shows the net worth of a fictional person. When you click on a column in the initial view, the example drills down into a second view that shows the change in value of a particular asset class over time.

The following example uses the Event object to get a reference to the clicked ColumnSeries. It then drills down into the single ColumnSeries by replacing the chart's series Array with the single ColumnSeries in the chart. When you click a column again, the chart returns to its original configuration with all ColumnSeries.

<?xml version="1.0"?> 
<!-- charts/SimpleDrillDown.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="initApp();srv.send();" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/networth-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/networth.aspx -->  
    </fx:Declarations> 
 
    <fx:Script><![CDATA[ 
        public var initSeriesArray:Array = new Array(); 
        public var level:Number = 1; 
        public var newSeries:Array; 
        
        private function initApp():void { 
            // Get initial series Array -- to be reloaded when it returns 
            // from a drill down. 
            initSeriesArray = chart.series;             
        } 
        
        private function zoomIntoSeries(e:Event):void { 
            newSeries = new Array(); 
            if (level == 1) { 
                newSeries.push(e.currentTarget);   
                level = 2; 
            } else { 
                newSeries = initSeriesArray; 
                p1.title = "Net Worth"; 
                level = 1; 
            }           
            chart.series = newSeries;            
        } 
        
    ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel id="p1" title="Net Worth"> 
         <s:layout> 
             <s:VerticalLayout/> 
         </s:layout> 
        <mx:ColumnChart id="chart" 
            dataProvider="{srv.lastResult.data.result}" 
            type="stacked" 
            showDataTips="true"> 
            <mx:series> 
                <mx:ColumnSeries id="s1" 
                    displayName="Cash" 
                    yField="cash" 
                    xField="date" 
                    click="zoomIntoSeries(event)"/> 
                <mx:ColumnSeries id="s2" 
                    displayName="Stocks" 
                    yField="stocks"  
                    xField="date"  
                    click="zoomIntoSeries(event)"/> 
                <mx:ColumnSeries id="s3" 
                    displayName="Retirement" 
                    yField="retirement" 
                    xField="date" 
                    click="zoomIntoSeries(event)"/> 
                <mx:ColumnSeries id="s4" 
                    displayName="Home" 
                    yField="home" 
                    xField="date" 
                    click="zoomIntoSeries(event)"/> 
                <mx:ColumnSeries id="s5" 
                    displayName="Other" 
                    yField="other" 
                    xField="date" 
                    click="zoomIntoSeries(event)"/> 
            </mx:series>            
            <mx:horizontalAxis > 
                <mx:DateTimeAxis title="Date" dataUnits="months"/> 
            </mx:horizontalAxis>    
        </mx:ColumnChart> 
        <mx:Legend dataProvider="{chart}"/> 
    </s:Panel> 
</s:Application>

Another approach to drilling down into chart data is to use unused data within the existing data provider. You can do this by changing the properties of the series and axes when the chart is clicked.

The following example is similar to the previous example in that it drills down into the assets of a fictional person's net worth. In this case, though, it shows the value of the asset classes for the clicked-on month in the drill-down view rather than the change over time of a particular asset class.

This example uses the HitData object's item property to access the values of the current data provider. By building an Array of objects with the newly-discovered data, the chart is able to drill down into the series without making calls to any external services.

Drilling down into data is an ideal time to use effects such as SeriesSlide. This example also defines seriesIn and seriesOut effects to slide the columns in and out when the drilling down (and the return from drilling down) occurs.

<?xml version="1.0"?> 
<!-- charts/DrillDownWithEffects.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();" 
    height="600"> 
     
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/networth-xml.aspx" result="resultHandler()"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/networth.aspx -->  
        <mx:SeriesSlide id="slideIn" duration="1000" direction="down"/> 
        <mx:SeriesSlide id="slideOut" duration="1000" direction="up"/> 
 
    </fx:Declarations> 
 
    <fx:Script><![CDATA[ 
        import mx.collections.ArrayCollection; 
        import mx.charts.HitData; 
        import mx.charts.events.ChartItemEvent; 
          
        [Bindable] 
        public var drillDownDataSet:ArrayCollection; 
 
        [Bindable] 
        public var dp:ArrayCollection; 
 
          // level is a temporary variable used to determine when to drill down. 
          private var level:int =  1; 
 
        private function resultHandler():void { 
          dp = ArrayCollection(srv.lastResult.data.result); 
        } 
        
        private function zoomIntoSeries(e:ChartItemEvent):void { 
            if (level == 1) { 
                drillDownDataSet = new ArrayCollection(genData(e)); 
                cs1.displayName = "Assets"; 
                cs1.yField = "amount"; 
                cs1.xField = "type"; 
 
                ca1.categoryField = "type"; 
                
                p1.title = "Asset breakdown for " + e.hitData.item.date; 
                dp = drillDownDataSet; 
                
                // Remove the Legend. It is not needed in this view because 
                // each asset class is its own column in the drill down. 
                p1.removeElement(myLegend); 
                
                level = 2; 
            } else { 
                cs1.displayName = "All Assets"; 
                cs1.yField = "assets"; 
                cs1.xField = "date"; 
                
                ca1.categoryField = "date"; 
                
                p1.title = "Net Worth"; 
                
                // Add the Legend back to the Panel. 
                p1.addElement(myLegend); 
                
                // Reset chart to original data provider. 
                resultHandler(); 
                
                level = 1; 
            } 
        } 
 
          // Build an ArrayCollection of objects to make up the new data set. 
        private function genData(e:ChartItemEvent):Array { 
                var result:Array = []; 
 
                trace("Cash: " + e.hitData.item.cash); 
                trace("Stocks: " + e.hitData.item.stocks); 
                trace("Retirement: " + e.hitData.item.retirement); 
                trace("Home: " + e.hitData.item.home); 
                trace("Other: " + e.hitData.item.other); 
                
                var o1:Object = { 
                    type:"cash", 
                    amount:e.hitData.item.cash 
                };                      
                var o2:Object = { 
                    type:"stocks", 
                    amount:e.hitData.item.stocks 
                };                      
                var o3:Object = { 
                    type:"retirement", 
                    amount:e.hitData.item.retirement 
                };                      
                var o4:Object = { 
                    type:"home", 
                    amount:e.hitData.item.home 
                };                      
                var o5:Object = { 
                    type:"other", 
                    amount:e.hitData.item.other 
                };                      
                trace(o1.type + ":" + o1.amount); 
                
                result.push(o1); 
                result.push(o2); 
                result.push(o3); 
                result.push(o4); 
                result.push(o5); 
                return result;               
        } 
        
    ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel id="p1" title="Net Worth"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:ColumnChart id="chart" 
            showDataTips="true" 
            itemClick="zoomIntoSeries(event)" 
            dataProvider="{dp}"> 
            <mx:series> 
                <mx:ColumnSeries id="cs1" 
                    displayName="All Assets" 
                    yField="assets" 
                    xField="date" 
                    hideDataEffect="slideOut" 
                    showDataEffect="slideIn"/> 
            </mx:series>            
            <mx:horizontalAxis> 
                <mx:CategoryAxis id="ca1" categoryField="date"/> 
            </mx:horizontalAxis> 
        </mx:ColumnChart> 
        
        <mx:Legend id="myLegend" dataProvider="{chart}"/> 
    </s:Panel> 
</s:Application>

Another way to drill down into chart data is to use the selection API. You can select a subset of data points on a chart to create a new data provider. For more information, see Selecting chart items.

Selecting chart items

As part of interacting with charts, you can select data points (ChartItem objects), examine the underlying data, and then perform actions on those objects. A ChartItem class represents a single entry in the chart series' data provider. A series is made up of an Array of ChartItem objects.

To enable data point selection, you set the value of the selectionMode property on the chart. Possible values of this property are none, single, and multiple. Setting the selectionMode property to none prevents any data points in the chart from being selected. Setting selectionMode to single lets you select one data point at a time. Setting selectionMode to multiple lets you select one or more data points at a time. The default value is none.

You also toggle the series' selectability by setting the value of the selectable property. As a result, while you might set the selectionMode on the chart to multiple, you can make the data points in some series selectable and others not selectable within the same chart by using the series' selectable property.

Methods of chart item selection

You can programmatically select data points or the user can interactively select data points in the following ways:

  • Mouse selection—Move the mouse pointer over a data point and click the left mouse button to select data points. For more information, see Keyboard and mouse selection.

  • Keyboard selection—Use the keyboard to select one or more data points, as described in Keyboard and mouse selection.

  • Region selection—Draw a rectangle on the chart. This rectangle defines the range, and selects all data points within that range. For more information, see Region selection.

  • Programmatic selection—Programmatically select one or more data points using the chart selection API. For more information, see Programmatic selection.

When selecting chart items, you should also understand the following terms:

  • caret—The current chart item that could be selected or deselected if you press the space bar. If you select multiple items, one at a time, the caret is the last item selected. If you selected multiple items by using a range, the caret is the last item in the last series in the selection. This is not always the item that was selected last.

  • anchor—The starting chart item for a multiple selection operation.

The following example creates three different types of charts. You can select one or more data points in each chart using the mouse, keyboard, and region selection techniques. The example displays the data points in each series that are currently selected.

<?xml version="1.0" ?> 
<!--  charts/SimpleSelection.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv_budget.send();srv_medals.send();srv_expenses.send()" 
    height="950" width="750"> 
 
    <fx:Declarations> 
        <!-- View source of the following pages to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv_medals" url="http://examplesserver/chart_examples/medals-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/medals.aspx -->  
        <mx:HTTPService id="srv_budget" url="http://examplesserver/chart_examples/budget-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/budget.aspx -->  
        <mx:HTTPService id="srv_expenses" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import mx.collections.ArrayCollection; 
 
        private function handleChange(event:Event, ta:spark.components.TextArea):void { 
            var allSeries:Array = event.currentTarget.series; 
            ta.text = ""; 
            for (var i:int=0; i<allSeries.length; i++) { 
                ta.text += "\n" + allSeries[i].id + 
                    " Selected Items: " + allSeries[i].selectedIndices; 
            } 
        } 
    ]]> 
    </fx:Script> 
    
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel title="Bar Chart" width="100%"> 
         <s:layout> 
             <s:HorizontalLayout/> 
         </s:layout> 
         <s:VGroup> 
 
 
 
 <mx:BarChart id="myBarChart" 
 height="225" 
 showDataTips="true" 
 dataProvider="{srv_medals.lastResult.data.result}" 
 selectionMode="multiple" 
 change="handleChange(event, textArea1)"> 
 
 
 
 
 <mx:verticalAxis> 
 
 
 
 
 
 <mx:CategoryAxis categoryField="country"/> 
 </mx:verticalAxis> 
 
 
 
 
 <mx:series> 
 
 
 
 
 
 <mx:BarSeries id="barSeries1" 
 yField="country" 
 xField="gold" 
 displayName="Gold" 
 selectable="true"/> 
 <mx:BarSeries id="barSeries2" 
 yField="country" 
 xField="silver" 
 displayName="Silver" 
 selectable="true"/> 
 <mx:BarSeries id="barSeries3" 
 yField="country" 
 xField="bronze" 
 displayName="Bronze" 
 selectable="true"/> 
 </mx:series> 
 
 
 
 </mx:BarChart> 
 
 
 
 <mx:Legend dataProvider="{myBarChart}" direction="horizontal"/> 
         </s:VGroup> 
         <s:VGroup width="250" height="200"> 
               <s:Label text="Click a chart's item to select it."/> 
               <s:Label text="Click and drag to select multiple items."/> 
               <s:Label text="Selection Changed Event:"/> 
               <s:TextArea id="textArea1" height="100" width="213"/> 
           </s:VGroup> 
    </s:Panel> 
 
    <s:Panel title="Pie Chart" width="100%"> 
         <s:layout> 
             <s:HorizontalLayout/> 
         </s:layout> 
         <s:VGroup> 
 
 
 
 <mx:PieChart id="myPieChart" 
 height="225" 
 
 
 
 
 dataProvider="{srv_budget.lastResult.data.result}" 
 showDataTips="true" 
 
 
 
 
 selectionMode="multiple" 
 change="handleChange(event, textArea2)"> 
 
 
 
 
 <mx:series> 
 
 
 
 
 
 <mx:PieSeries id="pieSeries1" 
 
 
 
 
 
 
 field="amount" 
 nameField="item" 
 labelPosition="callout"/> 
 
 
 
 
 </mx:series> 
 
 
 
 </mx:PieChart> 
 
         <mx:Legend dataProvider="{myPieChart}" direction="horizontal"/> 
         </s:VGroup> 
         <s:VGroup width="250" height="200"> 
            <s:Label text="Click a chart's item to select it."/> 
            <s:Label text="Click and drag to select multiple items."/> 
            <s:Label text="Selection Changed Event:"/> 
            <s:TextArea id="textArea2" height="100" width="213"/> 
         </s:VGroup> 
    </s:Panel>    
    <s:Panel title="Plot Chart" width="100%"> 
         <s:layout> 
             <s:HorizontalLayout/> 
         </s:layout> 
         <s:VGroup> 
 
 
 
 <mx:PlotChart id="myPlotChart" 
 height="225" 
 showDataTips="true" 
 dataProvider="{srv_expenses.lastResult.data.result}" 
 selectionMode="multiple" 
 change="handleChange(event, textArea3)"> 
 
 
 
 
 <mx:series> 
 
 
 
 
 
 <mx:PlotSeries id="plotSeries1" 
 xField="expenses" 
 yField="profit" 
 displayName="Expenses/Profit" 
 selectable="true"/> 
 <mx:PlotSeries id="plotSeries2" 
 xField="amount" 
 yField="expenses" 
 displayName="Amount/Expenses" 
 selectable="true"/> 
 <mx:PlotSeries id="plotSeries3" 
 xField="profit" 
 yField="amount" 
 displayName="Profit/Amount" 
 selectable="true"/> 
 </mx:series> 
 
 
 
 </mx:PlotChart> 
 
         <mx:Legend dataProvider="{myPlotChart}" direction="horizontal"/> 
          </s:VGroup> 
          <s:VGroup width="250" height="200"> 
               <s:Label text="Click a chart's item to select it."/> 
               <s:Label text="Click and drag to select multiple items."/> 
               <s:Label text="Selection Changed Event:" /> 
               <s:TextArea id="textArea3" height="100" width="213"/> 
          </s:VGroup> 
    </s:Panel> 
</s:Application>

You can specify the colors that are used to indicate if an item is selected, disabled, or being rolled over. You do this by using the itemSelectionColor, itemDisabledColor, and itemRollOverColor style properties of the chart controls. The following example specifies different colors for these states for several chart types:

 BarChart, PieChart, PlotChart { 
 	itemSelectionColor: red; 
 	itemDisabledColor: green; 
 	itemRollOverColor: blue;       
 }

Region selection

Users can select all data points in an area on a chart by drawing a rectangle (the region) on the chart. Users define a rectangular region by clicking and holding the mouse button while they move the mouse, drawing a rectangle on the chart. On the MOUSE_UP event, the items inside the rectangular region are selected. The starting point for a rectangular range must be over the chart. You cannot start the rectangle outside of the chart control's bounds.

To select data points with region selection, the value of the chart's selectionMode property must be multiple. If it is single or none, then you cannot draw the selection rectangle on the chart.

For most charts, as long as any point inside the rectangle touches a data point (for example, a column in a ColumnChart control), you select that data point. For BubbleChart and PieChart controls, an item is selected only if its center is inside the selection rectangle.

Keyboard and mouse selection

To select chart items, you can use the arrow keys, space bar, and enter key on your keyboard or the mouse pointer. If you want to select more than data point, the value of the chart's selectionMode property must be multiple.

Left and right arrow keys

You use the left and right arrows to move up and down the data in the series.

Click the left arrow to select the previous item and the right arrow to select the next item in the series. When you reach the last item in a series, you move to the first item in the next series.

Up and down arrow keys

Click the up and down arrow keys to move to the next or previous series on the chart.

Click the up arrow key to select the next series in the chart's series array. Click the down arrow key to select the previous series.

The index of the item in each series remains the same. If there is only one series in the chart, then click the up arrow key to select the first data point in the series; click the down arrow key to select the last data point in the series.

Shift and control keys

You can use the shift and control keys in conjunction with the other keys to select or deselect data points and change the caret in the selection.

Hold the shift key down while clicking the right arrow key to add the next data point to the selection until you reach the end of the series. Click the left arrow key to remove the last data point from the selection.

Hold the shift key down while clicking the up arrow key to select the first item in the next series. Click the down arrow key to select the last item in the previous series.

Hold the control key down while using the arrow keys to move the caret while not deselecting the anchor item. You can then select the new caret by pressing the space bar.

Space bar

Click the space bar to toggle the selection of the caret item, if the control key is depressed. A deselected caret item appears highlighted, but the highlight color is not the same as the selected item color.

Page Up/Home and Page Down/End keys

Click the Page Up/Home and Page Down/End keys to move to the first and last items in the current series, respectively. Moving to an item makes that item the caret item, but does not select it. You can press the space bar to select it.

Mouse pointer

The default behavior of the mouse pointer is to select the data point under the mouse pointer when you click the mouse button. This also de-selects all other data points.

If you click the mouse button and drag it over a chart, you create a rectangular region that defines a selection range. All data points inside that range are selected if the selectionMode property is set to multiple.

If you select a data point, then hold the shift key down and click on another data point, you select the first point, the target point, and all points that appear inside the rectangular range that you just created. If you select a data point, then hold the control key down and click another data point, you select both data points, but not the data points in between. You can add more individual data points by continuing to hold the control key down while you select another data point.

If you click anywhere on the chart that does not have a data point without any keys held down, then you clear the selection. Clicking outside of the chart control's boundary does not clear the selection.

Programmatic selection

You can use the chart selection APIs to programmatically select one or more data points in a chart control. These APIs consist of methods and properties of the ChartBase, ChartItem, and chart series objects.

Selections with multiple items include a caret and an anchor. You can access these items by using the caretItem and anchorItem properties of the chart control.

Properties of the series

The series defines which ChartItem objects are selected. You can programmatically select items by setting the values of the following properties of the series:

  • selectedItem

  • selectedItems

  • selectedIndex

  • selectedIndices

The index properties refer to the index of the chart item in the series. This index is the same as the index in the data provider, assuming you did not sort or limit the series items.

Programmatically setting the values of these properties does not trigger a change event.

The following example increments and decrements the series' selectedIndex property to select each item, one after the other, in the series:

<?xml version="1.0" ?> 
<!--  charts/SimplerCycle.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();initApp();" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import mx.charts.chartClasses.ChartBase; 
        import mx.charts.ChartItem; 
        import mx.charts.series.items.ColumnSeriesItem;        
        
        private function initApp():void { 
            // Select the first item on start up. 
            series1.selectedIndex = 0; 
        } 
        
        private function getNext(e:Event):void { 
            series1.selectedIndex += 1; 
        } 
 
        private function getPrev(e:Event):void { 
            series1.selectedIndex -= 1; 
        } 
 
        private function getFirst(e:Event):void { 
            series1.selectedIndex = 0; 
        } 
 
        private function getLast(e:Event):void { 
            series1.selectedIndex = series1.items.length - 1; 
        } 
 
    ]]> 
    </fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel height="100%" width="100%"> 
         <s:layout> 
             <s:VerticalLayout/> 
         </s:layout> 
        <mx:ColumnChart id="myChart" 
            height="207" width="350" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="single"> 
            <mx:series> 
                <mx:ColumnSeries id="series1" 
                    yField="expenses" 
                    displayName="Expenses" 
                    selectable="true"/> 
            </mx:series> 
            <mx:horizontalAxis> 
                <mx:CategoryAxis categoryField="month"/> 
            </mx:horizontalAxis>            
        </mx:ColumnChart> 
 
        <s:HGroup> 
            <s:Label id="label0" text="Value: "/> 
            <s:Label id="label1"/> 
        </s:HGroup>      
    
        <mx:Legend dataProvider="{myChart}" width="200"/> 
        <s:HGroup> 
            <s:Button label="|&lt;" click="getFirst(event);" /> 
            <s:Button label="&lt;" click="getPrev(event);" /> 
            <s:Button label="&gt;" click="getNext(event);" /> 
            <s:Button label="&gt;|" click="getLast(event);" /> 
        </s:HGroup> 
    </s:Panel> 
</s:Application>

To cycle through ChartItem objects in a series, you can use methods such as getNextItem() and getPreviousItem(). For more information, see Methods and properties of the ChartBase class.

The selectedIndices property lets you select any number of ChartItems in a chart control. The following example uses the selectedIndices property to select all items in all series when the user presses the Ctrl+a keys on the keyboard:

<?xml version="1.0" ?> 
<!--  charts/SelectAllItems.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();initApp();" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import flash.events.KeyboardEvent; 
        import mx.charts.events.ChartItemEvent; 
        import mx.core.FlexGlobals; 
        
        private function initApp():void { 
            FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_UP, keyHandler); 
        } 
        
        private function keyHandler(event:KeyboardEvent):void { 
            var ctrlPressed:Boolean = event.ctrlKey; 
            
            // If the user presses Ctrl + A, select all chart items. 
            if (ctrlPressed) { 
                var curKeyCode:int = event.keyCode; 
                if (curKeyCode == 65) { // 65 is the keycode value for 'a' 
                    selectItems(); 
                } 
            } 
        } 
        
        private function selectItems():void { 
            // Create an array of all the chart's series.           
            var allSeries:Array = myChart.series; 
            
            // Iterate over each series.           
            for (var i:int=0; i<allSeries.length; i++) { 
                var selectedData:Array = []; 
                
                // Iterate over the number of items in the series. 
                for (var j:int=0; j<srv.lastResult.data.result.length; j++) { 
                        selectedData.push(j); 
                } 
                
                // Use the series' selectedIndices property to select all the 
                // chart items. 
                allSeries[i].selectedIndices = selectedData; 
            } 
        }        
    ]]> 
    </fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel height="100%" width="100%"> 
         <s:layout> 
             <s:VerticalLayout/> 
         </s:layout> 
        <mx:PlotChart id="myChart" 
            height="207" 
            width="350" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="multiple"> 
            <mx:series> 
                <mx:PlotSeries id="series1" 
                    xField="expenses" 
                    yField="profit" 
                    displayName="Expenses/Profit" 
                    selectable="true"/> 
                <mx:PlotSeries id="series2" 
                    xField="amount" 
                    yField="expenses" 
                    displayName="Amount/Expenses" 
                    selectable="true"/> 
                <mx:PlotSeries id="series3" 
                    xField="profit" 
                    yField="amount" 
                    displayName="Profit/Amount" 
                    selectable="true"/> 
            </mx:series> 
        </mx:PlotChart> 
        
        <mx:Legend dataProvider="{myChart}" width="200"/>         
        <s:Label text="Click Ctrl + A to select all items."/> 
    </s:Panel> 
</s:Application>

The following example is similar to the previous example, except that it lets you set a threshold value in the TextInput control. The chart only selects chart items whose values are greater than that threshold.

<?xml version="1.0" ?> 
<!--  charts/ConditionallySelectChartItems.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx" result="resultHandler()"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
        <![CDATA[ 
            import mx.collections.ArrayCollection; 
 
            [Bindable] 
            private var expensesAC:ArrayCollection; 
 
            private function resultHandler():void { 
              expensesAC = ArrayCollection(srv.lastResult.data.result); 
            } 
 
            private function selectItems(event:Event):void { 
                // Create an array of all the chart's series.           
                var allSeries:Array = myChart.series; 
 
                // Iterate over each series.           
                for (var i:int=0; i<allSeries.length; i++) { 
                    var selectedData:Array = []; 
                    // Iterate over each item in the series. 
                    for (var j:int=0; j<expensesAC.length; j++) { 
                        if (expensesAC.getItemAt(j).expenses >= 
                            Number(threshold.text)) { 
                            selectedData.push(j); 
                        } 
                    } 
 
                    // Use the series' selectedIndices property to select all the 
                    // chart items that met the criteria. 
                    allSeries[i].selectedIndices = selectedData; 
                } 
            }        
        ]]> 
    </fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel height="100%" width="100%"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:PlotChart id="myChart" 
            height="207" 
            width="350" 
            showDataTips="true" 
            dataProvider="{expensesAC}" 
            selectionMode="multiple"> 
            <mx:series> 
                <mx:PlotSeries id="series1" 
                    xField="expenses" 
                    yField="profit" 
                    displayName="Expenses/Profit" 
                    selectable="true"/> 
                <mx:PlotSeries id="series2" 
                    xField="amount" 
                    yField="expenses" 
                    displayName="Amount/Expenses" 
                    selectable="true"/> 
                <mx:PlotSeries id="series3" 
                    xField="profit" 
                    yField="amount" 
                    displayName="Profit/Amount" 
                    selectable="true"/> 
            </mx:series> 
        </mx:PlotChart> 
        <mx:Legend dataProvider="{myChart}" width="200"/> 
        
        <s:HGroup> 
            <s:Label text="'Expenses' Threshold:"/> 
            <s:TextInput id="threshold" text="1500"/>        
        </s:HGroup> 
        <s:Button label="Select Items" click="selectItems(event)"/> 
        
    </s:Panel> 
</s:Application>

Methods and properties of the ChartBase class

The ChartBase class is the parent class of all chart controls. You can programmatically access ChartItem objects by using the following methods of this class:

  • getNextItem()

  • getPreviousItem()

  • getFirstItem()

  • getLastItem()

These methods return a ChartItem object, whether it is selected or not. Which object is returned depends on which one is currently selected, and which direction constant (ChartBase.HORIZONTAL or ChartBase.VERTICAL) you pass to the method.

The following example uses these methods to cycle through the data points in a ColumnChart control. It sets the value of the series' selectedItem property to identify the new ChartItem as the currently selected item.

<?xml version="1.0" ?> 
<!--  charts/SimpleCycle.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx" result="resultHandler()"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import mx.charts.chartClasses.ChartBase; 
        import mx.charts.ChartItem; 
        import mx.charts.series.items.ColumnSeriesItem;  
        import mx.collections.ArrayCollection; 
        
        [Bindable] 
        private var myAC:ArrayCollection = new ArrayCollection(); 
        
        private function resultHandler():void { 
            myAC = srv.lastResult.data.result; 
            series1.selectedIndex = 0; 
        } 
        
        private function getNext(e:Event, dir:*):void { 
            // Ensure that Flash Player doesn't try to select items outside of the bounds. 
            if (series1.selectedIndex < series1.items.length - 1) { 
                var curItem:ChartItem = series1.selectedItem;      
                var newItem:ChartItem = myChart.getNextItem(dir); 
                applyNewItem(newItem); 
            } 
        } 
 
        private function getPrev(e:Event, dir:*):void { 
            // Ensure that Flash Player doesn't try to select items outside of the bounds. 
            if (series1.selectedIndex > 0) { 
                var curItem:ChartItem = series1.selectedItem;            
                var newItem:ChartItem = myChart.getPreviousItem(dir); 
                applyNewItem(newItem); 
            } 
        } 
 
        private function getFirst(e:Event, dir:*):void { 
            var newItem:ChartItem = myChart.getFirstItem(dir); 
            applyNewItem(newItem); 
        } 
 
        private function getLast(e:Event, dir:*):void { 
            var newItem:ChartItem = myChart.getLastItem(dir); 
            applyNewItem(newItem); 
        } 
 
        private function applyNewItem(n:ChartItem):void { 
            trace(series1.selectedIndex + " of " + series1.items.length); 
            series1.selectedItem = n;       
        } 
    ]]> 
    </fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel height="100%" width="100%"> 
         <s:layout> 
             <s:VerticalLayout/> 
         </s:layout> 
        <mx:ColumnChart id="myChart" 
            height="207" 
            width="350" 
            showDataTips="true" 
            dataProvider="{myAC}" 
            selectionMode="single"> 
            <mx:series> 
                <mx:ColumnSeries id="series1" 
                    yField="expenses" 
                    displayName="Expenses" 
                    selectable="true"/> 
            </mx:series>          
            <mx:horizontalAxis> 
                <mx:CategoryAxis categoryField="month"/> 
            </mx:horizontalAxis>                     
        </mx:ColumnChart> 
    
        <mx:Legend dataProvider="{myChart}" width="200"/> 
        <s:HGroup> 
            <s:Button label="|&lt;" 
                click="getFirst(event, ChartBase.HORIZONTAL);"/> 
            <s:Button label="&lt;" 
                click="getPrev(event, ChartBase.HORIZONTAL);"/> 
            <s:Button label="&gt;" 
                click="getNext(event, ChartBase.HORIZONTAL);"/> 
            <s:Button label="&gt;|" 
                click="getLast(event, ChartBase.HORIZONTAL);"/> 
        </s:HGroup> 
    </s:Panel> 
</s:Application>

These item getter methods do not have associated setters. You cannot set the selected ChartItem objects in the same manner. Instead, you use the properties of the series, as described in Properties of the series.

Calling the getNextItem() and getPreviousItem() methods provides more control over the item selection than simply incrementing and decrementing the series' selectedIndex property as shown in Properties of the series. With these methods, you can choose the direction in which you select the next or previous item, and then decide whether to make the item appear selected.

The item getter methods also account for when you get to the end of the series, for example. If no ChartItems are currently selected, then the getNextItem() method gets the first one in the series. If you are at the beginning of the series, the getPreviousItem() gets the last item in the series.

The ChartBase class defines two additional properties, selectedChartItem and selectedChartItems, that return a ChartItem object or an array of ChartItem objects that are selected. You cannot set the values of these properties to select chart items. These properties are read-only. They are useful if your chart has multiple series and you want to know which items across the series are selected without iterating over all the series.

Setting the current state of a ChartItem object

You can set the value of the currentState property of a ChartItem object to make it appear selected or deselected, or make it appear in some other state. The currentState property can be set to none, rollOver, selected, disabled, focusedSelected, and focused.

Setting the state of the item does not add it to the selectedItems array. It only changes the appearance of the chart item. Setting the value of this property also does not trigger a change event.

Defined range

You can use the getItemsInRegion() method to define a rectangular area and select those chart items that are within that region. The getItemsInRegion() method takes an instance of the Rectangle class as its only argument. This rectangle defines an area on the stage, in global coordinates.

Getting an array of ChartItem objects with the getItemsInRegion() method does not trigger a change event. The state of the items does not change.

The following example lets you specify the x, y, height, and width properties of a rectangular range. It then calls the getItemsInRegion() method and passes those values to define the range. All chart items that fall within this invisible rectangle are selected and their values are displayed in the TextArea control.

<?xml version="1.0" ?> 
<!--  charts/GetItemsInRangeExample.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600" width="600"> 
     
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/medals-xml.aspx"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/medals.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import mx.charts.chartClasses.ChartBase; 
        import mx.charts.ChartItem; 
        import mx.charts.series.items.ColumnSeriesItem;        
        
        private function getItems(e:Event):void { 
            var x:Number = Number(ti1.text); 
            var y:Number = Number(ti2.text); 
            var h:Number = Number(ti3.text); 
            var w:Number = Number(ti4.text); 
        
            var r:Rectangle = new Rectangle(x, y, h, w); 
            
            // Get an Array of ChartItems in the defined area. 
            var a:Array = myChart.getItemsInRegion(r); 
            
            for (var i:int = 0; i<a.length; i++) {              
                var myChartItem:ColumnSeriesItem = ColumnSeriesItem(a[i]); 
            
                // Make items appear selected. 
                myChartItem.currentState = "selected"; 
            
                // Show values of the items that appear selected. 
                ta1.text += myChartItem.xValue.toString() + 
                    "=" + myChartItem.yValue.toString() + "\n"; 
            } 
        }        
    ]]> 
    </fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel id="p1" title="Selecting Items in Ranges"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:ColumnChart id="myChart" 
            height="207" width="350" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="multiple"> 
            <mx:series> 
                <mx:ColumnSeries id="series1" 
                    yField="gold" 
                    displayName="Gold" 
                    selectable="true"/> 
                <mx:ColumnSeries id="series2" 
                    yField="silver" 
                    displayName="Silver" 
                    selectable="true"/> 
                <mx:ColumnSeries id="series3" 
                    yField="bronze" 
                    displayName="bronze" 
                    selectable="true"/> 
            </mx:series>            
            <mx:horizontalAxis> 
                <mx:CategoryAxis categoryField="country"/> 
            </mx:horizontalAxis>                        
        </mx:ColumnChart> 
 
        <mx:Legend dataProvider="{myChart}" direction="horizontal"/> 
        
<s:Label text="Specify dimensions of a rectangle:"/> 
        <s:HGroup> 
            <s:Form> 
                <s:FormItem label="x"> 
                    <s:TextInput id="ti1" text="20" width="50"/> 
                </s:FormItem> 
                <s:FormItem label="y"> 
                    <s:TextInput id="ti2" text="20" width="50"/> 
                </s:FormItem> 
            </s:Form> 
            <s:Form> 
                <s:FormItem label="Height"> 
                    <s:TextInput id="ti3" text="200" width="50"/> 
                </s:FormItem> 
                <s:FormItem label="Width"> 
                    <s:TextInput id="ti4" text="200" width="50"/> 
                </s:FormItem> 
            </s:Form> 
        </s:HGroup> 
        <s:Button label="Select Items Inside Rectangle" click="ta1.text='';getItems(event)" /> 
        <s:TextArea id="ta1" height="100" width="300"/> 
    </s:Panel> 
</s:Application>

Working with ChartItem objects to access chart data

To work with the data of a ChartItem, you typically cast it to its specific series type. For example, in a ColumnChart control, the ChartItem objects are of type ColumnSeriesItem. Doing this lets you access series-specific properties such as the yValue, xValue, and index of the ChartItem. You can also then access the data provider's object by using the item property. Finally, you can access the properties of the series by casting the element property to a series object.

Assuming that a[i] is a reference to a ChartItem object from an Array, use the following syntax.

Access the properties of the series item

 var csi:ColumnSeriesItem = ColumnSeriesItem(a[i]); 
 trace(csi.yValue);

Access the item's fields

 var csi:ColumnSeriesItem = ColumnSeriesItem(a[i]); 
 trace(csi.item.Profit);

Access the series properties

 var csi:ColumnSeriesItem = ColumnSeriesItem(a[i]); 
 var cs:ColumnSeries = ColumnSeries(csi.element); 
 trace(cs.displayName);

About selection events

When a user selects a chart item using mouse, keyboard or region selection, the chart's change event is dispatched. When the following properties are updated through user interaction, a change event is dispatched:

  • selectedChartItem and selectedChartItems (on the chart)

  • selectedItem, selectedItems, selectedIndex, and selectedIndices (on the chart's series)

The change event does not contain references to the HitData or HitSet of the selected chart items. To access that information, you can use the chart's selectedChartItem and selectedChartItems properties.

When you select an item programmatically, no change event is dispatched. When you change the selectedState property of a chart item, no change event is dispatched.

Clearing selections

You can clear all selected data points by using the chart control's clearSelection() method. The following example clears all selected data points when the user presses the ESC key:

<?xml version="1.0" ?> 
<!--  charts/ClearItemSelection.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="initApp();srv.send();" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import flash.events.KeyboardEvent; 
        import mx.core.FlexGlobals; 
        
        private function initApp():void { 
            FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_UP, keyHandler); 
        } 
        
        // Clears all chart items selected when the user presses the ESC key. 
        private function keyHandler(event:KeyboardEvent):void { 
            var curKeyCode:int = event.keyCode; 
            if (curKeyCode == 27) { // 27 is the keycode value for ESC 
                myChart.clearSelection(); 
            } 
        } 
    ]]> 
    </fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel height="100%" width="100%"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:PlotChart id="myChart" 
            height="207" 
            width="350" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="multiple"> 
            <mx:series> 
                <mx:PlotSeries id="series1" 
                    xField="expenses" 
                    yField="profit" 
                    displayName="Expenses/Profit" 
                    selectable="true"/> 
                <mx:PlotSeries id="series2" 
                    xField="amount" 
                    yField="expenses" 
                    displayName="Amount/Expenses" 
                    selectable="true"/> 
                <mx:PlotSeries id="series3" 
                    xField="profit" 
                    yField="amount" 
                    displayName="Profit/Amount" 
                    selectable="true"/> 
            </mx:series> 
        </mx:PlotChart> 
        <mx:Legend dataProvider="{myChart}" width="200"/>  
        <s:Label text="Press ESC to clear selection."/>     
    </s:Panel> 
</s:Application>

In addition to clearing item selections programmatically, users can use the mouse or keyboard to clear items. If a user clicks anywhere on the chart control's background, and not over a data point, they clear all selections. If a user uses the up and down arrow keys to select a data point, they clear the existing data points. For more information, see Keyboard and mouse selection.

Using the selection API to create new charts

You can use the selection API to get some or all of the ChartItem objects of one chart, and then create a new chart with them. To do this, you create a new data provider for the new chart. To do this, you can call the ArrayCollection's getItemAt() method on the original chart's data provider and pass to it the original series' selected indices. This method then returns an object whose values you then add to an object in the new data provider.

The selectedIndex and selectedIndices properties of a chart's series represent the position of the chart item in the series. This position is also equivalent to the position of the chart item's underlying data object in the data provider.

The following example creates a PieChart control from the selected columns in the ColumnChart control. The PieChart control also allows selection; you can explode a piece by selecting it.

<?xml version="1.0" ?> 
<!--  charts/MakeChartFromSelection.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();initApp();" 
    height="600"> 
 
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx" result="resultHandler()"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
        <mx:SeriesInterpolate id="interpol" 
            duration="1000" 
            elementOffset="0" 
            minimumElementDuration="200"/>    
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import mx.collections.ArrayCollection; 
        import mx.charts.chartClasses.ChartBase; 
        import mx.charts.ChartItem; 
        import mx.charts.series.items.ColumnSeriesItem;        
        import mx.charts.PieChart; 
        import mx.charts.series.PieSeries; 
        import mx.charts.events.ChartItemEvent; 
        import mx.charts.Legend; 
 
        [Bindable] 
        public var expensesAC:ArrayCollection; 
 
        [Bindable] 
        public var newDataProviderAC:ArrayCollection; 
        
        private function resultHandler():void { 
               expensesAC = ArrayCollection(srv.lastResult.data.result); 
        } 
        
        private function initApp():void { 
            myColumnChart.addEventListener(ChartItemEvent.CHANGE, createNewChart); 
            setupPieChart(); 
        } 
        
        private function getNewDataProvider():ArrayCollection {            
            newDataProviderAC = new ArrayCollection(); 
            
            for (var i:int=0; i<series1.selectedItems.length; i++) { 
                var o:Object = new Object();                
                o.Month = expensesAC.getItemAt(series1.selectedIndices[i]).month; 
                o.Expenses = expensesAC.getItemAt(series1.selectedIndices[i]).expenses; 
                newDataProviderAC.addItem(o); 
            } 
            return newDataProviderAC; 
        } 
 
        private var newChart:PieChart; 
        private var newSeries:PieSeries; 
        
        [Bindable] 
        private var explodedPiece:Array; 
 
        private function explodePiece(e:Event):void { 
            explodedPiece = new Array();           
            explodedPiece[newSeries.selectedIndex] = .2; 
            newSeries.perWedgeExplodeRadius = explodedPiece; 
        } 
 
        private function setupPieChart():void {     
            newChart  = new PieChart(); 
            newChart.showDataTips = true;            
            newChart.selectionMode = "single"; 
 
            newSeries = new PieSeries(); 
            newSeries.field = "Expenses"; 
            newSeries.nameField = "Month"; 
            newSeries.setStyle("labelPosition", "callout"); 
            newSeries.setStyle("showDataEffect", "interpol"); 
 
            var newSeriesArray:Array = new Array(); 
            newSeriesArray.push(newSeries); 
            newChart.series = newSeriesArray; 
 
            newChart.addEventListener(ChartItemEvent.CHANGE, explodePiece); 
 
            // Create a legend for the new chart. 
            var newLegend:Legend = new Legend(); 
            newLegend.dataProvider = newChart; 
 
            p1.addElement(newChart); 
            p1.addElement(newLegend); 
        } 
 
        private function createNewChart(e:Event):void { 
            newChart.dataProvider = getNewDataProvider(); 
        } 
    ]]> 
    </fx:Script>   
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel id="p1" title="Column Chart"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:ColumnChart id="myColumnChart" 
            height="207" 
            width="350" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="multiple"> 
            <mx:series> 
                <mx:ColumnSeries id="series1" 
                    yField="expenses" 
                    displayName="Expenses" 
                    selectable="true"/> 
            </mx:series>            
            <mx:horizontalAxis> 
                <mx:CategoryAxis categoryField="month"/> 
            </mx:horizontalAxis>                        
        </mx:ColumnChart> 
    </s:Panel> 
</s:Application>

Dragging and dropping ChartItem objects

As an extension of the selection API, you can drag and drop chart items from one chart to another (or from a chart to some other object altogether).

To use drag and drop operations in your chart controls:

  • Make the source chart drag enabled by setting it's dragEnabled property to true.

  • Make the target chart drop enabled by setting it's dropEnabled property to true.

  • Add event listeners for the dragEnter and dragDrop events on the target chart.

  • In the dragEnter event handler, get a reference to the target chart and register it with the DragManager. To be a drop target, a chart must define an event handler for this event. You must call the DragManager.acceptDragDrop() method for the drop target to receive the drag and drop events.

  • In the dragDrop event handler, get an Array of chart items that are being dragged and add that Array to the target chart's data provider. You do this by using the event.dragSource.dataForFormat() method. You must specify the String "chartitems" as the argument to the dataForFormat() method. This is because the chart-based controls have predefined values for the data format of drag data. For all chart controls, the format String is "chartitems".

The following example lets you drag chart items from the ColumnChart control to the PieChart control. When the application starts, there is no PieChart control, but it becomes visible when the first chart item is dragged onto the container. This example also examines the dragged chart items to ensure that they are not added more than once to the target chart. It does this by using the target data provider's contains() method.

<?xml version="1.0" ?> 
<!--  charts/MakeChartFromDragDrop.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();initApp();" 
    height="600"> 
     
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx" result="resultHandler()"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
        <mx:SeriesInterpolate id="interpol" 
            duration="1000" 
            elementOffset="0" 
            minimumElementDuration="200" 
        /> 
 
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import mx.collections.ArrayCollection; 
        import mx.charts.chartClasses.ChartBase; 
        import mx.charts.ChartItem; 
        import mx.charts.PieChart; 
        import mx.charts.series.PieSeries; 
        import mx.charts.events.ChartItemEvent; 
        import mx.events.DragEvent; 
        import mx.controls.List; 
        import mx.managers.DragManager; 
        import mx.core.DragSource; 
        import mx.charts.Legend; 
 
        [Bindable] 
        public var newDataProviderAC:ArrayCollection; 
        
        [Bindable] 
        private var expensesAC:ArrayCollection; 
        
        private function initApp():void { 
            setupPieChart(); 
        } 
 
        private function resultHandler():void { 
               expensesAC = ArrayCollection(srv.lastResult.data.result); 
        } 
        
        private var newChart:PieChart; 
        private var newSeries:PieSeries; 
        
        [Bindable] 
        private var explodedPiece:Array; 
 
        private function explodePiece(e:Event):void { 
            explodedPiece = new Array();           
            explodedPiece[newSeries.selectedIndex] = .2; 
            newSeries.perWedgeExplodeRadius = explodedPiece; 
        } 
 
        private function setupPieChart():void {     
            newChart  = new PieChart(); 
            newChart.showDataTips = true;            
            newChart.selectionMode = "multiple"; 
            newChart.dropEnabled= true; 
            newChart.dragEnabled= false; 
            
            newChart.height = 350; 
            newChart.width = 350; 
            
            newChart.addEventListener("dragEnter", doDragEnter); 
            newChart.addEventListener("dragDrop", doDragDrop); 
 
            newChart.dataProvider = newDataProviderAC; 
 
            newSeries = new PieSeries(); 
            newSeries.field = "expenses"; 
            newSeries.nameField = "month"; 
            newSeries.setStyle("labelPosition", "callout"); 
            newSeries.setStyle("showDataEffect", "interpol"); 
 
            var newSeriesArray:Array = new Array(); 
            newSeriesArray.push(newSeries); 
            newChart.series = newSeriesArray; 
 
            newChart.addEventListener(ChartItemEvent.CHANGE, explodePiece); 
 
            // Create a legend for the new chart. 
            var newLegend:Legend = new Legend(); 
            newLegend.dataProvider = newChart; 
 
            p2.addElement(newChart); 
            p2.addElement(newLegend); 
        } 
 
        private function doDragEnter(event:DragEvent):void { 
            // Get a reference to the target chart. 
            var dragTarget:ChartBase = ChartBase(event.currentTarget); 
 
            // Register the target chart with the DragManager. 
            DragManager.acceptDragDrop(dragTarget); 
        } 
 
        private function doDragDrop(event:DragEvent):void { 
            // Get a reference to the target chart. 
            var dropTarget:ChartBase=ChartBase(event.currentTarget); 
 
            // Get the dragged items from the drag initiator. When getting 
            // items from chart controls, you must use the 'chartitems' 
            // format. 
            var items:Array = event.dragSource.dataForFormat("chartitems") 
                as Array; 
 
            // Trace status messages. 
            trace("length: " + String(items.length)); 
            trace("format: " + String(event.dragSource.formats[0])); 
    
            // Add each item to the drop target's data provider. 
            for(var i:uint=0; i < items.length; i++) { 
                
                // If the target data provider already contains the 
                // item, then do nothing. 
                if (dropTarget.dataProvider.contains(items[i].item)) { 
                
                // If the target data provider does NOT already 
                // contain the item, then add it. 
                } else { 
                    dropTarget.dataProvider.addItem(items[i].item);                
                }                
            } 
        } 
    ]]> 
    </fx:Script>   
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel id="p1" title="Source Chart" height="250" width="400"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:ColumnChart id="myColumnChart" 
            height="207" 
            width="350" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="multiple" 
            dragEnabled="true" 
            dropEnabled="false"> 
            <mx:series> 
                <mx:ColumnSeries id="series1" 
                    yField="expenses" 
                    displayName="Expenses" 
                    selectable="true"/> 
            </mx:series>            
            <mx:horizontalAxis> 
                <mx:CategoryAxis categoryField="month"/> 
            </mx:horizontalAxis>                        
        </mx:ColumnChart> 
    </s:Panel> 
    
    <!-- This will be the parent of the soon-to-be created chart. --> 
    <s:Panel id="p2" title="Target Chart" height="400" width="400"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Panel> 
</s:Application>

Chart controls also support the standard drag and drop methods of List-based controls such as hideDropFeedback() and showDropFeedback(). These methods display indicators under the mouse pointer to indicate whether drag and drop operations are allowed and where the items will be dropped.

For more information about drag and drop operations, see Drag and drop.

Dropping ChartItem objects onto components

The target of a drag and drop operation from a chart control does not need to be another chart control. Instead, you can drag an object onto any Flex component using simple drag and drop rules. The following example lets you drag a column from the chart onto the TextArea. The TextArea then extracts data from the dropped item and displays that information in text.

<?xml version="1.0" ?> 
<!--  charts/DragDropToComponent.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/medals-xml.aspx"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/medals.aspx -->  
    </fx:Declarations> 
 
    <fx:Script> 
    <![CDATA[ 
        import mx.collections.ArrayCollection; 
 
        import mx.events.DragEvent; 
        import mx.controls.List; 
        import mx.managers.DragManager; 
        import mx.core.DragSource; 
 
        import mx.charts.chartClasses.ChartBase; 
        import mx.charts.ChartItem; 
        import mx.charts.events.ChartItemEvent; 
        import mx.charts.series.items.ColumnSeriesItem; 
 
        private function doDragEnter(event:DragEvent):void { 
            var dragTarget:TextArea = TextArea(event.currentTarget); 
            DragManager.acceptDragDrop(dragTarget); 
        } 
 
        private function doDragDrop(event:DragEvent):void { 
            var dropTarget:TextArea = TextArea(event.currentTarget); 
            
            var curItem:ColumnSeriesItem = 
                ColumnSeriesItem(event.dragSource.dataForFormat("chartitems")[0]);            
            var curSeries:ColumnSeries = ColumnSeries(curItem.element); 
            
            var medalType:String = curSeries.displayName; 
            var numMedals:String = curItem.yValue.toString(); 
            var countryName:String = curItem.item.country; 
            
            ta1.text = countryName + " earned " + numMedals + " " + medalType + " medals."; 
        } 
    ]]> 
    </fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel title="Dropping ChartItem Objects"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <mx:ColumnChart id="myChart" 
            height="225" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="single" 
            dragEnabled="true"> 
            <mx:horizontalAxis> 
                <mx:CategoryAxis categoryField="country"/> 
            </mx:horizontalAxis> 
            <mx:series> 
                <mx:ColumnSeries id="columnSeries1" 
                    xField="country" 
                    yField="gold" 
                    displayName="Gold" 
                    selectable="true"/> 
                <mx:ColumnSeries id="columnSeries2" 
                    xField="country" 
                    yField="silver" 
                    displayName="Silver" 
                    selectable="true"/> 
                <mx:ColumnSeries id="columnSeries3" 
                    xField="country" 
                    yField="bronze" 
                    displayName="Bronze" 
                    selectable="true"/> 
            </mx:series> 
        </mx:ColumnChart> 
        <s:HGroup> 
            <mx:Legend dataProvider="{myChart}"/> 
            <s:TextArea id="ta1" 
                height="75" width="200" 
                dragEnter="doDragEnter(event)" 
                dragDrop="doDragDrop(event)"/> 
        </s:HGroup> 
    </s:Panel>    
</s:Application>

Changing the drag image

When you drag a chart item off of a source chart, an outline of the underlyig chart item appears as the drag image. For example, if you drag a column from a ColumnChart control, a column appears under the mouse pointer to represent the item that is being dragged.

You can customize the image that is displayed during a drag operation by overriding the default defintion of the dragImage property in a custom chart class. You do this by embedding your new image (or defining it in ActionScript), overriding the dragImage() getter method, and returning the new image.

The default location, in coordinates, of the drag image is 0,0 unless you override the DragManager's doDrag() method. You can also set the starting location of the drag proxy image by using the x and y coordinates of the image proxy in the dragImage() getter.

The following example custom chart class embeds an image and returns it in the dragImage() getter method. This example also positions the drag image proxy so that the mouse pointer is near its lower right corner.

// charts/MyColumnChart.as 
package { 
    import mx.charts.ColumnChart; 
    import mx.core.IUIComponent; 
    import mx.controls.Image; 
 
    public class MyColumnChart extends ColumnChart { 
        [Embed(source="images/dollarSign.png")] 
        public var dollarSign:Class;            
        public function MyColumnChart() { 
            super(); 
        } 
        
        override protected function get dragImage():IUIComponent { 
             var imageProxy:Image = new Image(); 
             imageProxy.source = dollarSign; 
             
             var imageHeight:Number = 50; 
             var imageWidth:Number = 32; 
             
             imageProxy.height = imageHeight; 
             imageProxy.width = imageWidth; 
 
             // Position the image proxy above and to the left of 
             // the mouse pointer.            
             imageProxy.x = this.mouseX - imageWidth; 
             imageProxy.y = this.mouseY - imageHeight; 
 
             return imageProxy; 
        } 
        
    } 
}

The following example uses the MyColumnChart example class to define its custom drag image:

<?xml version="1.0" ?> 
<!--  charts/DragDropCustomDragImage.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send();initApp();" 
    xmlns:local="*" 
    height="800"> 
 
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
        <mx:SeriesInterpolate id="interpol" 
            duration="1000" 
            elementOffset="0" 
            minimumElementDuration="200"/> 
    </fx:Declarations> 
    
    <fx:Script> 
    <![CDATA[ 
        import mx.collections.ArrayCollection; 
        import mx.charts.chartClasses.ChartBase; 
        import mx.charts.ChartItem; 
        import mx.charts.PieChart; 
        import mx.charts.series.PieSeries; 
        import mx.charts.events.ChartItemEvent; 
        import mx.events.DragEvent; 
        import mx.controls.List; 
        import mx.managers.DragManager; 
        import mx.core.DragSource; 
        import mx.controls.Image; 
        import flash.events.MouseEvent; 
        import mx.charts.Legend; 
 
        [Bindable] 
        public var newDataProviderAC:ArrayCollection; 
        
        [Bindable] 
        private var expensesAC:ArrayCollection = new ArrayCollection([ 
            { Month: "Jan", Expenses: 1500 }, 
            { Month: "Feb", Expenses: 200 }, 
            { Month: "Mar", Expenses: 500 }, 
            { Month: "Apr", Expenses: 1200 }, 
            { Month: "May", Expenses: 575 } ]); 
        
        private function initApp():void { 
            setupPieChart(); 
        } 
        
        private var newChart:PieChart; 
        private var newSeries:PieSeries; 
        
        [Bindable] 
        private var explodedPiece:Array; 
 
        private function explodePiece(e:Event):void { 
            explodedPiece = new Array();           
            explodedPiece[newSeries.selectedIndex] = .2; 
            newSeries.perWedgeExplodeRadius = explodedPiece; 
        } 
 
        private function setupPieChart():void {     
            newChart  = new PieChart(); 
            newChart.showDataTips = true;            
            newChart.selectionMode = "multiple"; 
            newChart.dropEnabled= true; 
            newChart.dragEnabled= false; 
 
            newChart.height = 350; 
            newChart.width = 350; 
            
            newChart.addEventListener("dragEnter", doDragEnter); 
            newChart.addEventListener("dragDrop", doDragDrop); 
 
            newChart.dataProvider = newDataProviderAC; 
 
            newSeries = new PieSeries(); 
            newSeries.field = "expenses"; 
            newSeries.nameField = "month"; 
            newSeries.setStyle("labelPosition", "callout"); 
            newSeries.setStyle("showDataEffect", "interpol"); 
 
            var newSeriesArray:Array = new Array(); 
            newSeriesArray.push(newSeries); 
            newChart.series = newSeriesArray; 
 
            newChart.addEventListener(ChartItemEvent.CHANGE, explodePiece); 
 
            // Create a legend for the new chart. 
            var newLegend:Legend = new Legend(); 
            newLegend.dataProvider = newChart; 
 
            p2.addElement(newChart); 
            p2.addElement(newLegend); 
        } 
 
        private function doDragEnter(event:DragEvent):void { 
            var dragTarget:ChartBase = ChartBase(event.currentTarget); 
            DragManager.acceptDragDrop(dragTarget); 
        } 
 
        private function doDragDrop(event:DragEvent):void { 
            var dropTarget:ChartBase=ChartBase(event.currentTarget); 
 
            var items:Array = event.dragSource.dataForFormat("chartitems") as Array; 
 
            for(var i:uint=0; i < items.length; i++) { 
                if (dropTarget.dataProvider.contains(items[i].item)) { 
                } else { 
                    dropTarget.dataProvider.addItem(items[i].item);                
                }                
            } 
        } 
    ]]> 
    </fx:Script>   
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
    <s:Panel id="p1" title="Source Chart" 
        height="250" width="400"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
        <local:MyColumnChart id="myChart" 
            height="207" width="350" 
            showDataTips="true" 
            dataProvider="{srv.lastResult.data.result}" 
            selectionMode="multiple" 
            dragEnabled="true" 
            dropEnabled="false"> 
            <local:series> 
                <mx:ColumnSeries id="series1" 
                    yField="expenses" 
                    displayName="Expenses" 
                    selectable="true"/> 
            </local:series>            
            <local:horizontalAxis> 
                <mx:CategoryAxis categoryField="month"/> 
            </local:horizontalAxis>                        
        </local:MyColumnChart> 
    </s:Panel> 
    
    <s:Panel id="p2" title="Target Chart" 
        height="400" width="400"> 
    </s:Panel> 
</s:Application>

Drawing on chart controls

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 MX 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 those used in MXML graphics. 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(), beginBitmapFill(), and endFill(). Convenience methods such as drawRect(), drawRoundedRect(), drawEllipse(), and drawCircle() are also available on the data canvases. For more information about MXML graphics, see MXML graphics.

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 from data canvases, use the removeChild(), removeChildAt(), and removeAllChildren() 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:

  • There is no drag-and-drop support for children of the data canvases.

  • You cannot use the chart selection APIs to select objects on the data canvases.

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 --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
 
    <fx:Declarations> 
         <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
         <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
         <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
  <fx:Script><![CDATA[ 
    import mx.charts.series.items.ColumnSeriesItem; 
    import mx.charts.ChartItem; 
     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 = "Expense: " + value1; 
        l3.text = "Month: " + month2; 
        l4.text = "Expense: " + 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.expenses;        
            chartHasLine = false; 
        } else { 
            s2 = sci.item.month; 
            v2 = sci.item.expenses; 
 
            connectTwoPoints(s1, v1, s2, v2);        
        } 
    } 
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Column Chart"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" 
        showDataTips="true" 
        dataProvider="{srv.lastResult.data.result}" 
        selectionMode="single" 
        change="handleChange(event)"> 
        <mx:annotationElements> 
            <mx:CartesianDataCanvas id="canvas" includeInRanges="true"/> 
        </mx:annotationElements> 
 
        <mx:horizontalAxis> 
           <mx:CategoryAxis categoryField="month"/> 
        </mx:horizontalAxis> 
 
        <mx:series> 
           <mx:ColumnSeries 
                id="series1" 
                xField="month" 
                yField="expenses" 
                displayName="Expense" 
                selectable="true"/> 
        </mx:series> 
     </mx:ColumnChart> 
     <mx:Legend dataProvider="{myChart}"/> 
     
     <s:HGroup> 
        <s:Button id="b1" 
            label="Connect Two Points" 
            click="connectTwoPoints('Jan', 1500, 'Mar', 500);"/> 
        <s:Button id="b2" 
            click="canvas.clear()" 
            label="Clear Line"/> 
     </s:HGroup> 
     
     <s:HGroup> 
         <s:VGroup> 
             <s:Label text="First Item"/> 
             <s:Label id="l1"/> 
             <s:Label id="l2"/> 
         </s:VGroup> 
 
         <s:VGroup> 
             <s:Label text="Second Item"/> 
             <s:Label id="l3"/> 
             <s:Label id="l4"/> 
         </s:VGroup> 
     </s:HGroup> 
   </s:Panel> 
</s:Application>

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 --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
    
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <fx:Script><![CDATA[ 
    import mx.charts.series.items.ColumnSeriesItem; 
    import mx.charts.ChartItem; 
 
     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); 
    } 
 
  ]]></fx:Script> 
  <s:Panel title="Column Chart"> 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
    <mx:ColumnChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        selectionMode="single" 
        change="handleChange(event)"> 
        <mx:annotationElements> 
            <mx:CartesianDataCanvas id="canvas" includeInRanges="true"/> 
        </mx:annotationElements> 
 
        <mx:horizontalAxis> 
           <mx:CategoryAxis 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}"/> 
     
     <s:Button id="b1" label="Connect Two Points" 
        click="connectTwoPoints('Jan', 2000, 'Mar', 1500);"/> 
     
     <s:HGroup> 
         <s:VGroup> 
             <s:Label text="First Item"/> 
             <s:Label id="l1"/> 
             <s:Label id="l2"/> 
         </s:VGroup> 
 
         <s:VGroup> 
             <s:Label text="Second Item"/> 
             <s:Label id="l3"/> 
             <s:Label id="l4"/> 
         </s:VGroup> 
     </s:HGroup> 
     
  </s:Panel> 
</s:Application>

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.

The following example uses the updateDataChild() method to add a label to a single data point on the line:
<?xml version="1.0"?> 
<!-- charts/UpdateDataChildExample.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    height="600"> 
 
  <fx:Script><![CDATA[ 
    import mx.controls.Label; 
    import mx.charts.chartClasses.CartesianCanvasValue; 
     import mx.collections.ArrayCollection; 
     [Bindable] 
     public var expenses: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} 
     ]); 
     
     public function drawData():void 
     { 
        canvas.clear(); 
        canvas.beginFill(0xFF0033, 1);      
        canvas.drawCircle("Feb", 1000, 20);      
        canvas.endFill(); 
        
        var myLabel:Label = new Label(); 
        myLabel.text = "X"; 
        myLabel.setStyle("fontWeight", "bold"); 
        myLabel.setStyle("fontSize", 20); 
        canvas.addChild(myLabel); 
        canvas.updateDataChild(myLabel, "Feb", 1100);   
     } 
     
  ]]></fx:Script> 
 
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
 
  <s:Panel title="Line Chart"> 
     <s:layout> 
         <s:HorizontalLayout/> 
     </s:layout> 
     <mx:LineChart id="myChart" dataProvider="{expenses}" 
        showDataTips="true" creationComplete="drawData()"> 
        <mx:annotationElements> 
            <mx:CartesianDataCanvas id="canvas" includeInRanges="true" /> 
        </mx:annotationElements> 
        <mx:horizontalAxis> 
           <mx:CategoryAxis dataProvider="{expenses}" categoryField="Month"/> 
        </mx:horizontalAxis> 
        <mx:series> 
           <mx:LineSeries yField="Profit" displayName="Profit"/> 
           <mx:LineSeries yField="Expenses" displayName="Expenses"/> 
        </mx:series> 
     </mx:LineChart> 
     <mx:Legend dataProvider="{myChart}"/> 
  </s:Panel> 
</s:Application>

Using offsets for data coordinates

The data canvas classes also let you add offsets to the position of data graphics. You do this by defining the data coordinates with the CartesianCanvasValue constructor rather than passing a data coordinate to the drawing or addDataChild() methods. When you define a data coordinate with the CartesianCanvasValue, you pass the data coordinate as the first argument, but you can pass an offset as the second argument.

The following example lets you specify an offset for the labels with an HSlider control. This offset is used when adding a data child to the canvas.

<?xml version="1.0"?> 
<!-- charts/AddLabelsWithOffsetLines.mxml --> 
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    creationComplete="srv.send()" 
    height="600"> 
    
    <fx:Declarations> 
        <!-- View source of the following page to see the structure of the data that Flex uses in this example. --> 
        <mx:HTTPService id="srv" url="http://examplesserver/chart_examples/expenses-xml.aspx"/> 
        <!-- To see data in an HTML table, go to http://examplesserver/chart_examples/expenses.aspx -->  
    </fx:Declarations> 
    
    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout> 
    
  <fx:Script><![CDATA[ 
    import mx.charts.series.items.ColumnSeriesItem; 
    import mx.charts.ChartItem; 
    import mx.charts.chartClasses.CartesianCanvasValue; 
    
     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 labelOffset:Number = 0; 
 
    [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; 
        
        // Use the CartesianCanvasValue constructor to specify 
        // an offset for data coordinates. 
        canvas.addDataChild(columnLabel, 
            new CartesianCanvasValue(s, labelOffset), 
            new CartesianCanvasValue(n, labelOffset) 
        ); 
    } 
 
  ]]></fx:Script> 
  <s:Panel title="Column Chart"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
     <mx:ColumnChart id="myChart" 
        dataProvider="{srv.lastResult.data.result}" 
        selectionMode="single" 
        change="handleChange(event)"> 
        <mx:annotationElements> 
            <mx:CartesianDataCanvas id="canvas" includeInRanges="true"/> 
        </mx:annotationElements> 
 
        <mx:horizontalAxis> 
           <mx:CategoryAxis 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}"/> 
     
     <s:Button id="b1" 
        label="Connect Two Points" 
        click="connectTwoPoints('Jan', 2000, 'Mar', 1500);"/> 
     
     <s:HGroup> 
         <s:VGroup> 
             <s:Label text="First Item"/> 
             <s:Label id="l1"/> 
             <s:Label id="l2"/> 
         </s:VGroup> 
 
         <s:VGroup> 
             <s:Label text="Second Item"/> 
             <s:Label id="l3"/> 
             <s:Label id="l4"/> 
         </s:VGroup> 
     </s:HGroup> 
     
    <mx:HSlider id="hSlider" minimum="-50" maximum="50" value="0" 
        dataTipPlacement="top" 
        tickColor="black" 
        snapInterval="1" tickInterval="10" 
        labels="['-50','0','50']" 
        allowTrackClick="true" 
        liveDragging="true" 
        change="labelOffset=hSlider.value"/>     
  </s:Panel> 
</s:Application>

Using multiple axes with data canvases

A CartesianDataCanvas is specific to a certain data space, which is defined by the bounds of the axis. If no axis is specified, the canvas uses the primary axes of the chart as its bounds. If you have a chart with multiple axes, you can specify which axes the data canvas should use by setting the values of the horizontalAxis or verticalAxis properties, as the following example illustrates:

 <mx:ColumnChart width="100%" height="100%" creationComplete="createLabel();"> 
 	<mx:annotationElements> 
 		<mx:CartesianDataCanvas id="canvas"  
 			includeInRanges="true"  
 			horizontalAxis={h1} 
 			verticalAxis={v1}/> 
 	</mx:annotationElements> 
 	... 
 </mx:ColumnChart>

Navigation

Using Flex » Using data-driven UI components

Adobe, Adobe Flash and Adobe Flash Player are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries and are used by permission from Adobe. No other license to the Adobe trademarks are granted.