[]
        
(Showing Draft Content)

Create Chart Callouts

Chart callouts are visual tools that help in emphasizing a specific series or data point on a chart, through a line or arrow from the point to be highlighted to a box with information text. These callouts not only supplement charts with additional information but also help in easy comprehension as they are directly connected to the point of emphasis. For instance, chart callouts can make it easy to indicate the values corresponding to maximum and minimum cost as shown in the example below.

chart callout tool

In FlexChart, you can create chart callouts using the Polygon type annotations by carrying out the following simple steps. In this example, we have demonstrated two type of callouts, one with simple line connector and other one with the arrow connector.

Steps to create chart callout

Set up the Application

  1. Create a new Windows Forms app.

  2. Drag and drop the FlexChart control from the toolbox onto the form.

    Observe: A column type chart is drawn with a default data.

Bind the FlexChart Control to a Data Source

  1. Create a data source.

    public class DataService
    {
        static Random rnd = new Random();
        public static List<UnitsCost> GetUnitCostData()
        {
            var data = new List<UnitsCost>();
            var date = new DateTime(2017, 1, 1);
            int cost = 900;
            for (int i = 10; i <= 180; i += 10)
            {
                cost += i <= 100 ? -rnd.Next(20, 70) : rnd.Next(20, 50);
                data.Add(new UnitsCost
                {
                    Units = i,
                    Cost = cost,
                });
            }
            return data;
        }
    }
    Public Class DataService
         Shared rnd As New Random()
         Public Shared Function GetUnitCostData() As List(Of UnitsCost)
              Dim data As List(Of UnitsCost) = New List(Of UnitsCost)()
              Dim [date] As DateTime = New DateTime(2017, 1, 1)
              Dim cost As Integer = 900
              For i As Integer = 10 To 180 Step 10
                   cost += If(i <= 100, -rnd.[Next](20, 70), rnd.[Next](20, 50))
                   data.Add(New UnitsCost() With {
                    .Units = i,
                    .Cost = cost
                   })
              Next
              Return data
         End Function
    End Class
  2. Bind the FlexChart to this data source by setting the DataSource property.

  3. Clear the default series getting displayed in the chart and add a new series using the Add method.

  4. Configure the X and Y axes by setting the BindingX and Binding property.

  5. Configure the chart by setting the ChartType and other required properties.

  6. Initialize the Rendering event of FlexChart to call the custom method SetupAnnotations which is implemented in the following steps to create the callouts.

    protected void SetupChart()
    {
           _data = DataService.GetUnitCostData();
    //     this.flexChart1.Header.Content = "Relationship between Production and Cost";
    
        this.flexChart1.Binding = "Cost";
        this.flexChart1.BindingX = "Units";
        this.flexChart1.DataSource = _data;
        this.flexChart1.ChartType = ChartType.LineSymbols;
        this.flexChart1.Series.Add(new Series() { Name = "Cost" });
        this.flexChart1.AxisX.Title = "Quantity";
        this.flexChart1.AxisY.Title = "Per Unit Cost";
        this.flexChart1.Rendering += FlexChart1_Rendering; 
    }
    Protected Sub SetupChart()
         _data = DataService.GetUnitCostData()
         '     this.flexChart1.Header.Content = "Relationship between Production and Cost";
    
         Me.flexChart1.Binding = "Cost"
         Me.flexChart1.BindingX = "Units"
         Me.flexChart1.DataSource = _data
         Me.flexChart1.ChartType = ChartType.LineSymbols
         Me.flexChart1.Series.Add(New Series() With {
          .Name = "Cost"
         })
         Me.flexChart1.AxisX.Title = "Quantity"
         Me.flexChart1.AxisY.Title = "Per Unit Cost"
         AddHandler Me.flexChart1.Rendering, AddressOf FlexChart1_Rendering
    End Sub

Create and Add the Annotations

  1. Create an annotation layer by creating an instance of the AnnotationLayer class.

  2. Create a line callout by creating an instance of the Polygon class.

  3. Specify the points to create the line callout and set the related properties to attach and style the annotation.

  4. Create a custom method, GetArrowCalloutPoints in this case, to measure the size of annotation text and calculate the coordinates for arrow callout annotation accordingly.

  5. Create an arrow callout by creating another instance of the Polygon class.

  6. Call the GetArrowCalloutPoints method and specify other related properties to attach and style the annotation.

  7. Add the two annotations in the annotation layer using the Add method.

    private void SetupAnnotations()
    {
        var annotationLayer = new AnnotationLayer(this.flexChart1);
        var orderedCost = _data.OrderBy(x => x.Cost).ToList();
        var arrowCallout = new Polygon("Maximum Cost")
        {
            Attachment = AnnotationAttachment.DataIndex,
            SeriesIndex = 0,
            PointIndex = _data.IndexOf(orderedCost[_data.Count - 1]),
        };
        arrowCallout.Style.FillColor = Color.FromArgb(100, Color.Pink);
        arrowCallout.Style.StrokeColor = Color.Red;
        arrowCallout.ContentStyle.StrokeColor = Color.Red;
        foreach (PointF point in GetArrowCalloutPoints(arrowCallout, orderedCost[_data.Count - 1]))
        {
            arrowCallout.Points.Add(point);
        }
        var lineCallout = new Polygon("Minimum Cost")
        {
            Attachment = AnnotationAttachment.DataIndex,
            SeriesIndex = 0,
            PointIndex = _data.IndexOf(orderedCost[0]),
            ContentCenter = new PointF(30, -60),
            Points = { new PointF(0, 0), new PointF(30, -40), new PointF(-30, -40), new PointF(-30, -80), new PointF(90, -80), new PointF(90, -40), new PointF(30, -40) }
        };
        lineCallout.Style.FillColor = Color.FromArgb(100, Color.Aqua);
        lineCallout.Style.StrokeColor = Color.Blue;
        lineCallout.ContentStyle.StrokeColor = Color.Blue;
        annotationLayer.Annotations.Add(arrowCallout);
        annotationLayer.Annotations.Add(lineCallout);
    }
    Private Sub SetupAnnotations()
         Dim annotationLayer As AnnotationLayer = New AnnotationLayer(Me.flexChart1)
         Dim orderedCost As List(Of UnitsCost) = _data.OrderBy(Function(x) x.Cost).ToList()
         Dim arrowCallout As Polygon = New Polygon("Maximum Cost") With {
          .Attachment = AnnotationAttachment.DataIndex,
          .SeriesIndex = 0,
          .PointIndex = _data.IndexOf(orderedCost(_data.Count - 1))
         }
         arrowCallout.Style.FillColor = Color.FromArgb(100, Color.Pink)
         arrowCallout.Style.StrokeColor = Color.Red
         arrowCallout.ContentStyle.StrokeColor = Color.Red
         For Each point As PointF In GetArrowCalloutPoints(arrowCallout, CType(orderedCost(_data.Count - 1), UnitsCost))
              arrowCallout.Points.Add(point)
         Next
         Dim lineCallout As Polygon = New Polygon("Minimum Cost") With {
          .Attachment = AnnotationAttachment.DataIndex,
          .SeriesIndex = 0,
          .PointIndex = _data.IndexOf(CType(orderedCost(0), UnitsCost)),
          .ContentCenter = New PointF(30, -60)
         }
         lineCallout.Points.Add(New PointF(0, 0))
         lineCallout.Points.Add(New PointF(30, -40))
         lineCallout.Points.Add(New PointF(-30, -40))
         lineCallout.Points.Add(New PointF(-30, -80))
         lineCallout.Points.Add(New PointF(-30, -80))
         lineCallout.Points.Add(New PointF(90, -80))
         lineCallout.Points.Add(New PointF(90, -40))
         lineCallout.Points.Add(New PointF(30, -40))
         lineCallout.Style.FillColor = Color.FromArgb(100, Color.Aqua)
         lineCallout.Style.StrokeColor = Color.Blue
         lineCallout.ContentStyle.StrokeColor = Color.Blue
         annotationLayer.Annotations.Add(arrowCallout)
         annotationLayer.Annotations.Add(lineCallout)
    End Sub

Note that the custom method GetArrowCalloutPoints used in the step above to get the size of the annotation text and to calculate the polygon coordinates based on that can be implemented as follows.

Render the Callouts

  1. Invoke the SetupAnnotations method in the Rendering event of the FlexChart class.

    private void FlexChart1_Rendering(object sender, RenderEventArgs e)
    {
        if (_renderEngine == null)
        {
            _renderEngine = e.Engine;
            SetupAnnotations();
        }
    }
    Private Sub FlexChart1_Rendering(sender As Object, e As RenderEventArgs)
         If _renderEngine Is Nothing Then
              _renderEngine = e.Engine
              SetupAnnotations()
         End If
    End Sub
  2. Run the sample to render the chart with callouts.

Observe that a chart displaying a simple line callout and an arrow callout is displayed to indicate the data points related to minimum and maximum cost. Similarly, you can create callouts in the form of other polygons by measuring the size of text to be used and calculating the coordinates of the Polygon annotations accordingly. For detailed implementation, see FlexChartExplorer sample which is shipped with the control. To see this feature in action, you can also download the FlexChartExplorer demo from our website.

See Also

Annotations