ComponentOne FlexChart for WinForms
Walkthrough / Create Chart Callouts
In This Topic
    Create Chart Callouts
    In This Topic

    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.

    Note: WinForms .NET 5 Edition has only runtime assemblies. Due to the new design-time model in VS2019 Preview, which is not complete yet from the Microsoft side, we do not supply any special design-time features as of yet. However, some of the controls might show up at design-time and allow editing few properties in the property grid.
    See Also

    Elements