Skip to content

TTMSFNCChart

Virtual vs Collection based mode

When dropping a new instance of the TTMSFNCChart on the form, the chart is initialized with some sample data, this contains a set of points added through the Points collection. This is called a collection-based mode which is also the default mode. When taking a look at the events, you will notice that some events have a virtual equivalent that is only called when implementing a virtual mode. The reason for having these events is to make a clear difference between virtual and collection based modes whenever a point is passed through as a parameter. All the other events can access the internal record data that holds a reference to the point collection item (Reference property), or the virtual point record data (VirtualReference property).

The virtual mode is enabled as soon as you implement the OnGetNumberOfPoints. Virtual mode is a global chart mode, so it is not possible to combine a collection-based and a virtual mode series. After implementing the OnGetNumberOfPoints, the OnGetPoint event is called to retrieve the data for a point. This is done through a record that can be directly accessed and manipulated. The advantage is that the event signature will not change when adding more properties in the future. There is no difference between virtual and collection-based mode in terms of series. The series are through the Series collection. Below is a sample that demonstrates this.

const
 PointArray: array[0..10] of Double = (10.5, 40.4, 3, 15, 60, 18, 34, 
40.5, 15.9, 35, 4);

procedure TForm1.FormCreate(Sender: TObject);
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Series.Clear;
 TMSFNCChart1.Series.Add;
 TMSFNCChart1.EndUpdate;
end;

procedure TForm1.TMSFNCChart1GetNumberOfPoints(Sender: TObject;
 ASerie: TTMSFNCChartSerie; var ANumberOfPoints: Integer);
begin
 ANumberOfPoints := Length(PointArray);
end;

procedure TForm1.TMSFNCChart1GetPoint(Sender: TObject;
 ASerie: TTMSFNCChartSerie; AIndex: Integer;
 var APoint: TTMSFNCChartPointVirtual);
begin
 APoint.YValue := PointArray[AIndex];
 APoint.XValue := AIndex;
end;

VC

The virtual equivalent for annotations is available through the OnGetNumberOfAnnotations and OnGetAnnotation events as demonstrated in the sample below.

const
 PointArray: array[0..10] of Double = (10.5, 40.4, 3, 15, 60, 18, 34, 
40.5, 15.9, 35, 4);

procedure TForm1.FormCreate(Sender: TObject);
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Series.Clear;
 TMSFNCChart1.Series.Add;
 TMSFNCChart1.EndUpdate;
end;

procedure TForm1.TMSFNCChart1GetAnnotation(Sender: TObject;
 ASerie: TTMSFNCChartSerie; APoint: TTMSFNCChartPointVirtual; AIndex:
Integer;
 var AAnnotation: TTMSFNCChartAnnotationVirtual);
begin
 AAnnotation.Text := 'Hello World !';
end;

procedure TForm1.TMSFNCChart1GetNumberOfAnnotations(Sender: TObject;
 ASerie: TTMSFNCChartSerie; APoint: TTMSFNCChartPointVirtual;
 var ANumberOfAnnotations: Integer);
begin
 if APoint.Index = 7 then
 ANumberOfAnnotations := 1;
end;

procedure TForm1.TMSFNCChart1GetNumberOfPoints(Sender: TObject;
 ASerie: TTMSFNCChartSerie; var ANumberOfPoints: Integer);
begin
 ANumberOfPoints := Length(PointArray);
end;

procedure TForm1.TMSFNCChart1GetPoint(Sender: TObject;
 ASerie: TTMSFNCChartSerie; AIndex: Integer;
 var APoint: TTMSFNCChartPointVirtual);
begin
 APoint.YValue := PointArray[AIndex];
 APoint.XValue := AIndex;
end;

VC1

Persistence

The chart is capable of saving its published properties (settings), to a file or stream. The format that is being used is JSON. To save the chart settings, use the code below.

TMSFNCChart1.SaveSettingsToFile();
TMSFNCChart1.SaveSettingsToStream();
To load an existing settings stream/file use the following code.
TMSFNCChart1.LoadSettingsFromFile();
TMSFNCChart1.LoadSettingsFromStream();
The Chart additionally exposes events to control which properties need to be saved to the settings file. In some circumstances, it might be required to only save a specific set of properties. The OnCanLoadProperty and OnCanSaveProperty events are responsible for this. Below is a sample that excludes a property ‘Extra’ from the persistence list.
procedure TForm1.TMSFNCChart1CanLoadProperty(Sender, AObject: TObject;
 APropertyName: string; APropertyType: TTypeKind; var ACanLoad: Boolean);
begin
 ACanLoad := ACanLoad and not (APropertyName = Extra);
end;
procedure TForm1. TMSFNCChart1CanSaveProperty(Sender, AObject: TObject;
 APropertyName: string; APropertyType: TTypeKind; var ACanSave: Boolean);
begin
 ACanSave := ACanSave and not (APropertyName = Extra);
end;
Please note that the above AND operation is crucial to maintain the existing exclusion list. Returning a true for each property will additionally save its default published properties such as Align, Position and many more.

Adding and removing series

The Chart has a collection of series that can be accessed programmatically or through the editor. The code below shows you how to add a new series based on a default Chart. With the code, the Chart is cleared, and a new series is added.

TMSFNCChart1.BeginUpdate;
TMSFNCChart1.Clear;
TMSFNCChart1.Series.Add;
TMSFNCChart1.EndUpdate;
To delete a series, you will need to know the index of the series you wish to delete. By default, the Chart adds 3 series with random values. With the following code, the last 2 series in the collection are removed.
TMSFNCChart1.BeginUpdate;
TMSFNCChart1.Series.Delete(1);
TMSFNCChart1.Series.Delete(1);
TMSFNCChart1.EndUpdate;
Adding and removing series

When adding a new series, the series does not contain any points, so the Chart will not draw any lines, bars or other chosen Chart types. Adding and removing points is explained in the chapter Adding and removing points.

Accessing series

When adding a new series, the series automatically adds an identifier, set with the LegendText property. This property is used in the legend, and in the editor. In code, you can access the series with the index in the collection, but more convenient, with a function called SerieByName. Below is a sample that demonstrates how this function can be used.

var
 I: Integer;
 s: TTMSFNCChartSerie;
 begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 TMSFNCChart1.Series.Add; //adds ‘Serie 0’ by default
 TMSFNCChart1.Series.Add; //adds ‘Serie 1’ by default

 s := TMSFNCChart1.SerieByName['Serie 0'];
 for I := 0 to 7 do
 s.AddPoint(Random(100));

 s := TMSFNCChart1.SerieByName['Serie 1'];
 for I := 0 to 7 do
 s.AddPoint(Random(100));

 TMSFNCChart1.EndUpdate;
end;

var
 I: Integer;
 s: TTMSFNCChartSerie;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.LegendText := 'Mercedes';
 TMSFNCChart1.EndUpdate;
end;
var
 s: TTMSFNCChartSerie;
begin
 s := TMSFNCChart1.SerieByName['Mercedes'];
 for I := 0 to 7 do
 s.AddPoint(Random(100));
end;

Adding and removing points

Adding or removing points is as easy as adding or removing series. Simply use the new series or retrieve an already existing series and use the Points collection property to add or remove new or existing points. By default, the Points collection already adds a random value to the YValue property. The XValue property is automatically incremented and has a direct relation to the number of points.

To add a new point, use the following code:

var
 s: TTMSFNCChartSerie;
begin
 TMSFNCChart1.BeginUpdate;
 s := TMSFNCChart1.Series.Add;
 s.Points.Add;
 TMSFNCChart1.EndUpdate;
end;
As explained, the point that is added to the series contains a random value. To change this value, define a variable that gives you access to the point properties like demonstrated in the code below.
var
 s: TTMSFNCChartSerie;
 pt: TTMSFNCChartPoint;
begin
 TMSFNCChart1.BeginUpdate;
 s := TMSFNCChart1.Series.Add;
 pt := s.Points.Add;
 pt.YValue := 123;
 TMSFNCChart1.EndUpdate;
end;
Each point has a value on the y-axis which is set with the YValue property, and a value on the x-axis. The value on the x-axis is set with the XValue property but is only used in XY type charts such as the ctXYLine or the ctXYMarker types.

An alternative to add a new point with a value is to use one of the AddPoint or AddXYPoint overloads that are publically accessible on serie level. The code below has an identical result as the previous code.

var
 s: TTMSFNCChartSerie;
begin
 TMSFNCChart1.BeginUpdate;
 s := TMSFNCChart1.Series.Add;
 s.AddPoint(Random(100));
 TMSFNCChart1.EndUpdate;
end;
To remove a point, simply use the same approach as removing a series.

Annotations

Annotations can be used to attach additional information to a specific point, shaped in a rectangle or an ellipse, with many customization options. Annotations are added and deleted in the same way as series, but on point level. Below is a sample which adds an annotation to a specific point in a line Chart.

var
 s: TTMSFNCChartSerie;
 I: Integer;
 an: TTMSFNCChartAnnotation;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 for I := 0 to 10 do
 begin
 s.Points.Add;
 if I = 7 then
 begin
 an := s.Points[I].Annotations.Add;
 an.Text := 'Hello World !';
end;
 end;
 TMSFNCChart1.EndUpdate;

Annotations

Annotations are auto-sized by default but can be configured to allow text alignment and wordwrapping.

Labels

Each series has the ability to show labels, which display a formatted string based on the YValue of the point. Labels have the same appearance, and are added to each point. Through events, labels can optionally be hidden, but are less configurable compared to annotations.

var
 s: TTMSFNCChartSerie;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 s.AutoYRange := arDisabled;
 s.MinY := 0;
 s.MaxY := 130;
 s.Labels.Visible := True;
 for I := 0 to 10 do
 s.Points.Add;
 TMSFNCChart1.EndUpdate;
end;

Labels

The labels shown in the sample are already formatted with the Delphi Format function which can be optionally modified to format floating point values or datetime values. The type of formatting can be changed with the FormatType property.

X-axis and y-axis values

By default, the Chart enables the x-axis and the y-axis for the first series only, but each series has its own x-axis and y-axis range and can configure the position and formatting for each axis separately. The amount of values that are shown depend on a number of properties, the available width / height, the major and minor unit and the font size are the most important properties.

Each series can position its x-axis top, center, bottom or a combination of those three values and the same applies for the y-axis, but with a left, center and right position. Further customization can be done with one of the various events for custom drawing, formatting, positioning, etc…

The x-axis has an additional feature that is based on the collection of points inside a series. Each point has an XValueText property that is linked to the XValue of that point. When the XValueText is set, the series will automatically detect and display the text at that point. Below is a sample which adds the months of the year as values of the x-axis.

var
 s: TTMSFNCChartSerie;
 I: Integer;
 pt: TTMSFNCChartPoint;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 s.AutoXRange := arEnabled;
 s.XValues.Angle := -90;
 for I := 1 to 12 do
 begin
 pt := s.Points.Add;
 pt.XValueText := FormatSettings.LongMonthNames[I];
 end;
 TMSFNCChart1.EndUpdate;
end;
X-axis and y-axis values

The values of the x-axis are now replaced with the months set via the XValueText property. In the sample, the values are rotated, because there isn’t enough room to place all values horizontally. This is achieved with the Angle property of the series object.

The MajorUnit and MinorUnit properties that are available for the x-axis values at serie level can be used to change the appearance. By default the MajorUnit is 1 and the MinorUnit is 0. The x-axis values do not automatically calculate the units as the y-axis does with the AutoUnits property. This property is false by default on the x-axis. Without a value assigned to the XValueText property, the x-axis will draw the floating point values with a specific formatting, based on the MajorUnit and MinorUnit.

Below is a sample which sets the MinorUnit to 0.5 for the x-axis.

var
 s: TTMSFNCChartSerie;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 s.AutoXRange := arEnabled;
 s.XValues.MinorUnit := 0.5;
 s.XValues.MinorUnitFormat := '%.1f';
 for I := 0 to 9 do
 s.Points.Add;
 TMSFNCChart1.EndUpdate;
end;
X-axis and y-axis values1

In this sample, we have also changed the MinorUnitFormat to display the fractional part of the MinorUnit set to 0.5.

The y-axis automatically calculates the best possible MajorUnit and MinorUnit by default. Changing the MajorUnit and MinorUnit on the y-axis will only be possible when the AutoUnits property is set to false.

var
 s: TTMSFNCChartSerie;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 s.AutoXRange := arEnabled;
 s.AutoYRange := arEnabledZeroBased;
 s.XValues.MinorUnit := 0.5;
 s.XValues.MinorUnitFormat := '%.1f';
 s.YValues.AutoUnits := False;
 s.YValues.MajorUnit := 10;
 s.YValues.MinorUnit := 5;
 for I := 0 to 9 do
 s.Points.Add;
 TMSFNCChart1.EndUpdate;
end;
X-axis and y-axis values2

When the default formatting, or adding text to a point with the XValueText property is not sufficient, you can implement an event that returns a string at a specific x-axis value. This can be applied for both the x-axis and the y-axis and is demonstrated in the code below based on the previous sample.

var
 s: TTMSFNCChartSerie;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 s.AutoXRange := arEnabled;
 s.AutoYRange := arEnabledZeroBased;
 s.XValues.MinorUnit := 0.5;
 s.XValues.MinorUnitFormat := '%.1f';
 s.YValues.AutoUnits := False;
 s.YValues.MajorUnit := 10;
 s.YValues.MinorUnit := 5;
 s.XValues.Angle := -90;
 for I := 0 to 9 do
 s.Points.Add;
 TMSFNCChart1.EndUpdate;
end;
procedure TForm1.TMSFNCChart1GetSerieXValue(Sender: TObject;
 ASerie: TTMSFNCChartSerie; AIndex: Integer;
 AKind: TTMSFNCChartDrawXYValueKind; AValue: Double; var AValueString:
string);
begin
 if (AKind = vkMajor) and (AValue = 6) then
 AValueString := 'Custom X Value';
end;
procedure TForm1.TMSFNCChart1GetSerieYValue(Sender: TObject;
 ASerie: TTMSFNCChartSerie; AIndex: Integer;
AKind: TTMSFNCChartDrawXYValueKind; AValue: Double; var AValueString:
string);
begin
 if (AKind = vkMinor) and (AValue = 25) then
 AValueString := 'Custom Y Value';
end;
X-axis and y-axis values3

Autorange

Each series has an AutoXRange and an AutoYRange property. By default the AutoXRange property is set to arDisabled and the AutoYRange is set to arEnabled. When one of those properties has a disabled auto range, the range is set with the MinX and MaxX properties for the x-axis, and the MinY and MaxY properties for the y-axis. Each range can be extended with the percentage variant for each property.

The autorange is especially useful for common ranges, ranges which have the same minimum and maximum for all series. Below is a sample with 2 series with random values which have a common range. This sample also shows each common range at the left and right side y-axis.

var
 s: TTMSFNCChartSerie;
 I, J: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 for I := 0 to 1 do
 begin
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 s.AutoYRange := arCommonZeroBased;
 s.XValues.MinorUnit := 0.5;
 s.XValues.MinorUnitFormat := '%.1f';
 s.YValues.AutoUnits := False;
s.YValues.MajorUnit := 10;
 s.YValues.MinorUnit := 5;
 if I = 0 then
 begin
 s.Stroke.Color := gcRed;
 s.YValues.Positions := [ypLeft, ypRight];
 s.XValues.Positions := [xpBottom];
 end
 else
 begin
 s.YValues.Positions := [];
 s.XValues.Positions := [];
 end;
 for J := 0 to 10 do
 s.Points.Add;
 end;
 TMSFNCChart1.EndUpdate;
end;
Autorange

Crosshair

The chart supports displaying and detecting values based on the mouse/cursor position. This feature can be enabled by setting

TMSFNCChart1.Crosshair.Visible := True;

There are 2 options available that can be used simultaneously.

  • Continuous (based on a series index, set with ContinuousSeriesIndex)
  • Point based (shows all matching point values, closest to the mouse position)

By default Continuous is activated. Based on a default chart with three series this produces the following result. As you can see, in the screenshot, there is a blue line, which matches the first series.

Crosshair

To switch to point based mode, set TMSFNCChart1.Crosshair.Modes := [ccmPointBased];. This will procude the following chart output:

Crosshair

The default formatting for the detected values are based on the YValues.MajorUnitFormatting and XValues.MajorUnitFormatting (and formatting type). The x-values also automatically detect labels and show the closest match possible. If for some reason you wish to change the formatting, or change the text, use the OnGetYAxisCrosshairText or OnGetXAxisCrosshairText events. The labels appearance can be configured for each series separately. By default the appearance follows the global appearance at chart level. If you want to add additional custom graphics, the OnBeforeDrawSerieXAxisCrosshairText, OnAfterDrawSerieXAxisCrosshairText,OnBeforeDrawSerieYAxisCrosshairText and OnAfterDrawSerieYAxisCrosshairText can be of help. The horizontal and vertical line drawn to indicate where the point is located can be further fine-tuned with the respective events, or via the properties at TMSFNCChart1.Series[Index].Crosshair. At this level, you can also exclude series from being detected by setting TMSFNCChart1.Series[Index].Crosshair.XPositions := []; and TMSFNCChart1.Series[Index].Crosshair.YPositions := [];. These properties are also used to add values to the center and right Y-Axis, and the top and center X-Axis if they are made visible at chart level.

Logarithmic scale

In some cases it's important to be able to display data with a very wide range of values in a single view. Typically, the largest values are 10, 100 or even 1000 times larger than the smallest values. They are typically referred to as exponential growth curves, or logarithmic scale charts. TTMSFNCChart is capable of configuring the X and Y scale in one of the following options:

  • Log-Log (both X and Y scale are logarithmic)
  • Semi-Log (X or Y scale is logarithmic)

The default logarithm base number is 10, which is the common logarithm. Enabling logarithmic scales, or changing the base number for both X and Y scale can be done with the following code. As you can see in the sample below, we change the Y scale base logarithmic value to 2, which maps on a binary logarithm.

TMSFNCChart1.Series[0].LogarithmicY := True;
TMSFNCChart1.Series[0].LogarithmicYBase := 2;

Semi-Log

To visualize a logarithmic scale, we start by creating a function in linear mode.

var
  s: TTMSFNCChartSerie;
  I: Integer;
begin
  TMSFNCChart1.Stroke.Kind := gskNone;
  TMSFNCChart1.Title.Line := False;
  Fill.Color := gcWhite;
  Fill.Kind := TBrushKind.Solid;

  TMSFNCChart1.BeginUpdate;
  TMSFNCChart1.Clear;
  s := TMSFNCChart1.Series.Add;

  s.AutoXRange := arEnabled;
  s.AutoYRange := arEnabled;
  s.XValues.AutoUnits := False;
  s.XValues.MajorUnit := 1;
  s.XValues.MinorUnit := 0;
  s.YValues.AutoUnits := True;
  s.Mode := smStatistical;

  for I := 0 to 14 do
    s.AddPoint(Power(2, I));

  TMSFNCChart1.EndUpdate;
end;
Chart Log

Now when we add the following 2 lines to the above code, we switch to a logarithmic scale

s.LogarithmicY := True;
s.LogarithmicYBase := 10;
We produce the following chart

Chart Log

You might notice the line is now linear, but the values matching the scale are logarithmic, this way, we can compact a chart and show more data in one view.

Log-Log

Log-Log is a chart where both X & Y scale are logarithmic. We can demonstrate this with the following code. Note that this requires the chart-type to be ctXYLine, because of the wide range of values

var
  s: TTMSFNCChartSerie;
  I: Integer;
begin
  TMSFNCChart1.Stroke.Kind := gskNone;
  TMSFNCChart1.Title.Line := False;
  Fill.Color := gcWhite;
  Fill.Kind := TBrushKind.Solid;

  TMSFNCChart1.BeginUpdate;
  TMSFNCChart1.Clear;
  s := TMSFNCChart1.Series.Add;
  s.LogarithmicY := True;
  s.LogarithmicYBase := 10;
  s.LogarithmicX := True;
  s.LogarithmicXBase := 10;
  s.ChartType := ctXYLine;


  s.AutoXRange := arEnabled;
  s.AutoYRange := arEnabled;
  s.XValues.AutoUnits := False;
  s.XValues.MajorUnit := 1;
  s.XValues.MinorUnit := 0;
  s.YValues.AutoUnits := True;
  s.Mode := smStatistical;

  for I := 0 to 14 do
  begin
    s.AddXYPoint(Power(2, I), Power(2, I));
  end;

  TMSFNCChart1.EndUpdate;
end;
Chart Log

Mathemathical vs Statistical

The Chart can display each series in a different mode. The default mode for each series is Mathematical. In mathematical mode, the X-axis zero value is at the crossing point of X-axis and Y-axis and thus the first value is displayed at the Y-axis. Further, it uses the complete available width of the series rectangle. The alternative is Statistical mode, which automatically calculates and applies an offset on the x-axis to evenly distribute the values across the available chart width.

Statistical Mathematical
Statistical Mathematical

Multi-Point Series

The Chart supports three types of multi-points series: ctOHLC, ctCandleStick and ctBoxPlot. Points can be added by using one of the AddMultiPoint overload methods. For the ctCandleStick and ctBoxPlot types, a separate increase and decrease fill and stroke color can be set under the series MultiPoints property. Below is a sample that demonstrates this.

var
 s: TTMSFNCChartSerie;
 c: Integer;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Series.Clear;
 TMSFNCChart1.SeriesMargins.Left := 10;
 TMSFNCChart1.SeriesMargins.Top := 10;
 TMSFNCChart1.SeriesMargins.Right := 10;
 TMSFNCChart1.SeriesMargins.Bottom := 10;
 s := TMSFNCChart1.Series.Add;
 s.ChartType := ctOHLC;
 s.AutoXRange := arCommonZeroBased;
 s.AutoYRange := arCommon;
 for I := 0 to 29 do
 begin
 c := Random(100);
 if Random(c) mod (Random(10) + 1) = 0 then
 s.AddMultiPoint(c + Random(20), c + 20, c - 20, c - Random(20))
 else
 s.AddMultiPoint(c - Random(20), c + 20, c - 20, c + Random(20));
end;
 TMSFNCChart1.EndUpdate;

Multi-Point Series

Pie

Changing the chart-type of one or multiple series to ctPie will automatically hide the x- and y-axis and the x- and y-grid. By default, the slices of the Pie will automatically take over the fill color of the series.

Pie

By default, the points that are added when initializing the series are reflected as separate slices. Adding points can be done with one of the AddPoint overloads. The general properties of a pie series can be changed at the Pie property. In the below sample, the main legend is hidden, the pie legend is shown and the colors for each slice are changed.

var
 s: TTMSFNCChartSerie;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Legend.Visible := False;
 TMSFNCChart1.Series.Clear;
 s := TMSFNCChart1.Series.Add;
 s.ChartType := ctPie;
 s.Pie.Size := 250;
 s.Points.Clear;
 s.Legend.Visible := True;
 s.Pie.AutoSize := False;
 s.Pie.Size := 250;
 s.Stroke.Color := gcBlack;
 s.AddPoint(Random(100) + 40, gcDarkred, 'Dark Red');
 s.AddPoint(Random(100) + 40, gcSteelblue, 'Steel Blue');
 s.AddPoint(Random(100) + 40, gcYellowgreen, 'Yellow Green');
 s.AddPoint(Random(100) + 40, gcLightseagreen, 'Light Sea Green');
 s.AddPoint(Random(100) + 40, gcOrange, 'Orange');
 TMSFNCChart1.EndUpdate;
end;
Pie1

Labels and annotations are supported in the same way as they are for the other chart types. You simply add annotations and/or turn on the labels by setting the visible property to true.

s.Labels.Visible := True;
Pie2

There are 2 variants for this charttype that allow handling more types of data. When setting the ChartType to ctSizedPie, the slices are equally divided, but the radius depends on the value of the point. When setting the ChartType to ctVariableRadiusPie, the slices are calculated as they are with a normal pie chart, but the radius can be controlled with an additional property called YValueVariable. This property can be accessed through the AddVariablePoint overload or directly at point level. Below is a sample of a ctSizedPie.

var
 s: TTMSFNCChartSerie;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Legend.Visible := False;
 TMSFNCChart1.Series.Clear;
 s := TMSFNCChart1.Series.Add;
 s.ChartType := ctSizedPie;
 s.Pie.Size := 400;
 s.Points.Clear;
 s.Legend.Visible := True;
 s.Pie.AutoSize := False;
 s.Pie.Size := 250;
 s.Stroke.Color := gcBlack;
 s.Labels.Visible := True;
 s.Labels.OffsetX := 0;
 s.Labels.OffsetY := 0;
 s.AddPoint(Random(100) + 75, gcDarkred, 'Dark Red');
 s.AddPoint(Random(100) + 75, gcSteelblue, 'Steel Blue');
 s.AddPoint(Random(100) + 75, gcYellowgreen, 'Yellow Green');
 s.AddPoint(Random(100) + 75, gcLightseagreen, 'Light Sea Green');
 s.AddPoint(Random(100) + 75, gcOrange, 'Orange');
 TMSFNCChart1.EndUpdate;
end;
Pie3

Spider

When changing the ChartType property to ctSpider, the properties of the YValues and YGrid properties are used to configure the visuals of the grid, while the start, sweep angles and dimensions are stored under the Pie property. Additionally, the Spider* properties under YValues and YGrid are used to further fine-tune spider chart specific features such as the grid kind and the values rotation angle. Applying the ctSpider chart type on the previous sample, changing the value labels, and applying the Spider properties to change Spider specific features generates the output below.

var
 s: TTMSFNCChartSerie;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Legend.Visible := False;
 TMSFNCChart1.Series.Clear;
 s := TMSFNCChart1.Series.Add;
 s.ChartType := ctSpider;
 s.Pie.Size := 400;
 s.Points.Clear;
 s.Legend.Visible := True;
 s.Pie.AutoSize := False;
 s.Pie.Size := 250;
 s.Fill.Opacity := 0.2; (FireMonkey only)
 s.Stroke.Color := gcBlack;
 s.Labels.Visible := True;
 s.Labels.OffsetX := 0;
 s.Labels.OffsetY := 0;
 s.YGrid.SpiderLegend := True;
 s.MaxY := 200;
 s.AutoYRange := arDisabled;
 s.YValues.AutoUnits := False;
 s.YValues.MajorUnit := 50;
 s.YValues.MinorUnit := 0;
 s.AddPoint(Random(100) + 75, 0, 'Value 1');
 s.AddPoint(Random(100) + 75, 0, 'Value 2');
 s.AddPoint(Random(100) + 75, 0, 'Value 3');
 s.AddPoint(Random(100) + 75, 0, 'Value 4');
 s.AddPoint(Random(100) + 75, 0, 'Value 5');
 TMSFNCChart1.EndUpdate;
end;
Spider

The ctSpider chart type follows the AutoYRange property to determine the maximum. When set to arDisabled (default), the MaxY property can be used to set a maximum for the grid drawing anspider chart calculation. Additionally, the enabled and common auto ranges can be used in combination with the stacked property on Pie level to compare multiple series in one chart. The above screenshot demonstrates this, and the code to accomplish this is demonstrated in the “Desktop” demo, available after installation.

Spider1

Legend

The legend displays the amount of series, each with their own legend text. If you want to display a legend for each series, with an entry for each point, you can turn off the Legend on chart level, and turn on the legend on series level. The properties are identical, and have the same customization events. In the previous chapter on the Pie chart type, the legend was displayed at the right side of the chart, with the typical chart type icon next to each entry. If you want further customization for this icon, you can override the OnBeforeDrawSerieLegendIcon. Below is a sample that demonstrates this.

procedure TForm1.TMSFNCChart1BeforeDrawSerieLegendIcon(Sender: TObject;
 ACanvas: TCanvas; ASerie: TTMSFNCChartSerie; APoint: TTMSFNCChartPoint;
 ARect: TRectF; var ADefaultDraw: Boolean);
begin
 ADefaultDraw := False;
 ACanvas.FillRect(ARect, 2, 2, AllCorners, 1);
 ACanvas.DrawRect(ARect, 2, 2, AllCorners, 1);
end;
Legend

Markers

Each series has the ability to show markers. Markers have an ellipse shape by default but can be changed to draw a triangle, square, diamond and an image. Further customization can be achieved through the OnBeforeDrawSerieMarker and the OnAfterDrawSerieMarker. Below is a sample that shows how to enable the markers on a serie and how to change the shape to a bitmap.

var
 s: TTMSFNCChartSerie;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
 s.Markers.Visible := True;
 s.AutoYRange := arDisabled;
 s.MinY := 0;
 s.MaxY := 100;
 for I := 0 to 10 do
 s.AddPoint(RandomRange(25, 75));
 TMSFNCChart1.EndUpdate;
end;
Markers

var
 s: TTMSFNCChartSerie;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.Mode := smStatistical;
s.Markers.Visible := True;
 s.Markers.Bitmap.LoadFromFile('icecream.png');
 s.Markers.Width := 32;
 s.Markers.Height := 32;
 s.Markers.Shape := msBitmap;
 s.AutoYRange := arDisabled;
 s.MinY := 0;
 s.MaxY := 100;
 for I := 0 to 10 do
 s.AddPoint(RandomRange(25, 75));
 TMSFNCChart1.EndUpdate;
end;
Markers1

Stacked series

When choosing a bar or area chart, and adding multiple series, you are able to combine those series in stacked variants based on the type of chart and the GroupIndex property on series level. The requirement is that each of those combined series have the same range, and have a value that is larger than 0. There are 2 types of stacked charts: the normal stacked charts combine the values for each group and the percentage stacked charts that represent the values of each series as a percentage of a maximum of 100. Below is a sample that adds 4 bar series in 2 stacked groups.

var
 s: TTMSFNCChartSerie;
 I, J: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 for I := 0 to 3 do
 begin
 s := TMSFNCChart1.Series.Add;
 s.ChartType := ctStackedBar;
 s.Mode := smStatistical;
s.AutoYRange := arCommonZeroBased;
 s.GroupIndex := I div 2;

 case I of
 1: s.Fill.Color := gcRed;
 2: s.Fill.Color := gcOrange;
 3: s.Fill.Color := gcBlue;
 end;

 s.Stroke.Color := gcBlack;

 if I > 0 then
 begin
 s.YValues.Positions := [];
 s.XValues.Positions := [];
 end;

 for J := 0 to 10 do
 s.AddPoint(RandomRange(25, 75));
 end;
 TMSFNCChart1.EndUpdate;
end;
Stacked series

In this sample, the GroupIndex is 0 for the first 2 series and 1 for the last 2 series resulting in a multi-group stacked bar series Chart.

Changing the GroupIndex property to 0 for all series gives the result below.

Stacked series1

For an area type, the GroupIndex doesn’t have any effect. With this type, all series are stacked like the previous sample with a GroupIndex that equals 0 for all series.

Stacked series2

The second type of stacked series are based on a minimum of 0 and a maximum of 100. The values that needs to be added can remain identical to the previous stacked chart and will internally be recalculated to match the 0 to 100 range. Below is a sample that demonstrates this.

var
 s: TTMSFNCChartSerie;
 I, J: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 for I := 0 to 3 do
 begin
s := TMSFNCChart1.Series.Add;
 s.ChartType := ctStackedPercentageBar;
 s.Mode := smStatistical;
 s.AutoYRange := arCommonZeroBased;
 s.GroupIndex := I div 2;

 case I of
 1: s.Fill.Color := gcRed;
 2: s.Fill.Color := gcOrange;
 3: s.Fill.Color := gcBlue;
 end;

 s.Stroke.Color := gcBlack;

 if I > 0 then
 begin
 s.YValues.Positions := [];
 s.XValues.Positions := [];
 end;

 for J := 0 to 10 do
 s.AddPoint(RandomRange(25, 75));
 end;

 TMSFNCChart1.EndUpdate;
end;
Stacked series3

3D

Each series has an Enable3D property that enables 2D rendering of a 3D like environment. The Offset3DX and Offset3DY properties on serie level determine the “depth” of the 3D visualization.Using the following code on an area chart, displays the chart with the following result:

var
 s: TTMSFNCChartSerie;
 I: Integer;
begin
 TMSFNCChart1.BeginUpdate;
 TMSFNCChart1.Clear;
 s := TMSFNCChart1.Series.Add;
 s.ChartType := ctArea;
 s.Mode := smStatistical;
 s.AutoYRange := arEnabledZeroBased;
 s.Enable3D := True;
 s.XGrid.Visible := True;
 s.YGrid.Visible := True;
s.Fill.Opacity := 0.5; (FireMonkey only)
 for I := 0 to 10 do
 s.AddPoint(RandomRange(25, 75));
 TMSFNCChart1.EndUpdate;
end;
3D

Interaction

The Chart supports interaction, which is enabled by default. Interaction enables click detection on a point or a bar and triggers the appropriate event. Additionally, panning and scaling can be performed in X and Y direction, depending on the property values under InteractionOptions. Below is a sample that implements the OnSerieBarClick event.

procedure TForm1.TMSFNCChart1SerieBarClick(Sender: TObject;
 APoint: TTMSFNCChartPoint);
begin
 ShowMessage('Bar with value ' + floattostr(APoint.YValue) + ' clicked 
!');
end;
Interaction

The alternative for all other chart types (except for the pie variants) is the OnSeriePointClick event which detects clicks on a point that lies within the ClickMargin boundaries relative from the point X and Y value. The ClickMargin is a property on Chart level. For the pie variants, the OnSerieSliceClick is executed.

Load Data

Note

More examples can be found in the Demos folder after installation.

From File, Stream or Text

There are a couple of ways that you can load data into your chart. Each of these methods can be used for a normal YValue, XValue and/or XLabel. The Ex-overload where you can add the YVariableValue and/or YSecondValue. And the Multi-Point overload where you set the high, low, open, close and if desired the median values.

When you call these methods you can use the DefaultLoadOptions or create a new instance of TTMSFNCChartLoadOptions and add this parameter to the call.

LoadFromDataArray, gives you the ability to load data in a serie or add a new serie with the data.

var
  loadOptions: TTMSFNCChartLoadOptions;
begin
  loadOptions := TTMSFNCChartLoadOptions.Create;
  loadOptions.YRange := arEnabledZeroBased;

  // Will change the values of the first Series in the chart, in case it doesn't exist, it will create a new.
  // We add the YValues, the XValues are nil and not used, and we set the XLabels, with the options that we've set.
  TMSFNCBarChart.LoadFromDataArray(loadOptions, 0, [123, 98, 54, 154, 128, 87, 103], nil, ['Apple', 'Watermelon', 'Pineapple', 'Pear', 'Banana', 'Lemon','Grapefruit', 'Peach']).LegendText := 'Sold';

  TMSFNCOHLCChart.LoadFromMultiPointDataArray(0, [82.63, 81.81, 82.44, 91.24, 95.92], [79.11, 79.99, 77.13, 75.71, 88.51], [81.56, 80.34, 80.94, 77.19, 91.01], [80.37, 80.94, 77.25, 91.00, 95.48]);
end;

LoadFromCSV, you can load data from a CSV-file by setting the indices of the different columns that you want to use. You can load the CSV from a file, url, stream or as text.

  // This method will create two series with the YValues and second values. The -1 values are the XValues and XLabels and those aren't used.
  TMSFNCBandChart.LoadFromCSVEx(myCSVStream, [0,2], -1, -1 [1,3]);

  // This method will create an additional series with the values in the file. The -1 value is the Median which isn't used.
  TMSFNCCandleStickChart.DefaultLoadOptions.ClearSeries := False;
  TMSFNCCandleStickChart.LoadFromCSVMultiPointData('myFile.csv', 3, 4, 2, 1, -1, 0);

LoadFromJSON, you can load the JSON from a file, url, stream or as text. The JSON values are set by choosing the name for each value. You can set the Series, Points, YValues are possible as a string or an array of strings if you have multiple values, ...

procedure Load;
begin
  TMSFNCBarChart1.LoadFromJSONData('my.json', 'series', 'points', 'sold', '', 'label', SeriesCallBack, PointsCallBack);
end;

procedure PointsCallBack(ASerie: TTMSFNCChartSerie; APoint: TTMSFNCChartPoint; APointJSONValue: TJSONValue);
var
  j: TJSONValue;
begin
    j := TTMSFNCUtils.GetJSONValue(APointJSONValue, 'fill');
    if Assigned(j) then
      APoint.Color :=  TTMSFNCUtils.GetJSONProp(APointJSONValue, 'fill');
end;
The JSON code used for this example:
{
  "series": {
    "type": "pie",
    "points": [
      {"label": "Apples", "sold": "128.14", "fill": "green"},
      {"label": "Oranges", "sold": "66.72", "fill": "orange"},
      {"label": "Lemons", "sold": "84.39", "fill": "yellow"}
    ],
    "other": "additional values"
  }
}

Load From Database

The chart can be filled with a dataset and the Adapter property set to an instance of the TTMSFNCChartDatabaseAdapter.

Load From TMS FNC Grid

This requires to have TMS FNC UI Pack installed as well. You can link the data from the cells of a TMSFNCGrid with Adapter property set to an instance of the TTMSFNCChartGridAdapter.