TCustomSeries.Clicked event in TChart is throw and fails

191 Views Asked by At

I'm using Borland C++Builder 2006 with TChart Standard 4.04, and I have a problem. I have a TChart that, when calculating where the user clicked on it, it fails doing this calculations, and it seems there is no way to avoid this problem. In fact, my feeling is that nobody clicks on the chart, and the event is thrown internally by itself.

Attached you can find the error details, and I would like to know what is doing exactly, in order to try to avoid the error. I have tried avoiding this problem disconnecting the series from the chart (ParentChart property to NULL while updating the content of the series), but it still happens.

Also, you can find, in the same attached image, the problematic chart (marked in red). Below, a little explanation of how is that chart. It contains 3 series:

  • 1 TPointSeries:

    • This series has only one point, in the same position that the selected one in next TPointSeries.

    • It shows bigger the selected point, and the top chart (which doesn't generate any problem) shows the details of the selected item.

  • 1 TPointSeries

    • This series represents the evolution, and has one point for each study carried out on a patient.
  • 1 TLineSeries

    • This series represents the evolution, and has one point for each study carried out on a patient.

    • At the end, it joins all the points of the previous series.

And here, is the image

image

I have been able to recreate the problem with the next simple unit. It contains a chart that shows 2 or 3 points. In order to change between 2 and 3 points, you can use buttons, or click on any point of the series. Each time that chart must be updated, all series are emptied, and filled again with the required data. In this state, it doesn't fail, independently if you use the buttons or clicks on series points.

But, the form has also a check box that, simply, adds a waiting of 1 second after updating the chart. In this case, when there are 3 points in the chart and you click on any point to change to 2 points, the chart is updated and stopped for 1 second. After this second, the error comes up.

Here, code of file .h:

//---------------------------------------------------------------------------
#ifndef MainH
#define MainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <vector.h>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Chart.hpp>
#include <ExtCtrls.hpp>
#include <Series.hpp>
#include <TeEngine.hpp>
#include <TeeProcs.hpp>
//---------------------------------------------------------------------------
struct TItemInChart {
    int IdMeasurement;
    int Group;
    TDateTime DateTime;
};

class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TChart *Chart;
    TLineSeries *Series_BgLine;
    TPointSeries *Series_Points;
    TPointSeries *Series_SelectedPoint;
    TButton *Button2p;
    TButton *Button3p;
    TCheckBox *cbSleepAfterUpdate;
    void __fastcall Button2pClick(TObject *Sender);
    void __fastcall ChartClickSeries(TCustomChart *Sender,
          TChartSeries *Series, int ValueIndex, TMouseButton Button,
          TShiftState Shift, int X, int Y);
    void __fastcall FormShow(TObject *Sender);
private:    // User declarations
    vector<TItemInChart*> vSetOf2Items;
    vector<TItemInChart*> vSetOf3Items;
public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Here, code of file .cpp:

//---------------------------------------------------------------------------
#include <vcl.h>
#include <Math.hpp>
#pragma hdrstop

#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{

    //Set of 2 elements
    TItemInChart *tf = new TItemInChart();
    tf->IdMeasurement = 21;
    tf->DateTime = Now();
    tf->Group = 1;
    vSetOf2Items.push_back(tf);

    tf = new TItemInChart();
    tf->IdMeasurement = 2;
    tf->DateTime = Now() + 1;
    tf->Group = 2;
    vSetOf2Items.push_back(tf);

    //Set of 3 elements
    tf = new TItemInChart();
    tf->IdMeasurement = 31;
    tf->DateTime = Now();
    tf->Group = 1;
    vSetOf3Items.push_back(tf);

    tf = new TItemInChart();
    tf->IdMeasurement = 32;
    tf->DateTime = Now() + 1;
    tf->Group = 2;
    vSetOf3Items.push_back(tf);

    tf = new TItemInChart();
    tf->IdMeasurement = 33;
    tf->DateTime = Now() + 2;
    tf->Group = 3;
    vSetOf3Items.push_back(tf);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2pClick(TObject *Sender)
{
    //Disconecting the series and the chart
    Series_BgLine->ParentChart = NULL;
    Series_Points->ParentChart = NULL;
    Series_SelectedPoint->ParentChart = NULL;

    //Deleting alle xisnting data
    Series_Points->Clear();
    Series_SelectedPoint->Clear();
    Series_BgLine->Clear();

    //Deciding which data I'm goping to load
    vector<TItemInChart*> vSetOfItems;
    if (Sender == Button2p) {
        vSetOfItems = vSetOf2Items;
    }
    else{
        vSetOfItems = vSetOf3Items;
    }

    //Loading the data.
    TItemInChart *iic;
    for (int i = 0; i < vSetOfItems.size(); i++){
        iic = vSetOfItems[i];

        //Adding to series
        Series_Points->AddXY(i+1, iic->Group, "", clRed);
        Series_BgLine->AddXY(i+1, iic->Group, "", clWhite);

        if ( (iic->IdMeasurement == 21) || (iic->IdMeasurement == 33) ) {
            //Adding to selected point serie.
            Series_SelectedPoint->Clear();
            Series_SelectedPoint->AddXY(i + 1, iic->Group, "", clRed);
        }
    }

    //Conencting again the series with the chart
    Series_BgLine->ParentChart = Chart;
    Series_Points->ParentChart = Chart;
    Series_SelectedPoint->ParentChart = Chart;

    //Waiting only if user wants
    if (cbSleepAfterUpdate->Checked) {
        Sleep(1000);
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ChartClickSeries(TCustomChart *Sender,
      TChartSeries *Series, int ValueIndex, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
    //When clicking on any serie point, we change from 2 to 3 points, and vice-versa. 
    if (Series_BgLine->Count() <= 2) {
        Button2pClick(Button3p);
    }
    else{
        Button2pClick(Button2p);
    }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
    //When starting, we fill the chart with 3 points
    Button2pClick(Button3p);
}
//---------------------------------------------------------------------------

Here, code of file .dfm:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 328
  ClientWidth = 556
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnShow = FormShow
  PixelsPerInch = 96
  TextHeight = 13
  object Chart: TChart
    Left = -11
    Top = 0
    Width = 565
    Height = 292
    AllowPanning = pmHorizontal
    AllowZoom = False
    BackWall.Brush.Color = clWhite
    BackWall.Color = 7436151
    BackWall.Pen.Color = clGray
    BottomWall.Color = 7436151
    Foot.AdjustFrame = False
    Foot.Visible = False
    MarginBottom = 3
    MarginLeft = 10
    MarginTop = 3
    Title.AdjustFrame = False
    Title.Color = 7436151
    Title.Font.Charset = DEFAULT_CHARSET
    Title.Font.Color = clWhite
    Title.Font.Height = -11
    Title.Font.Name = 'Arial'
    Title.Font.Style = []
    Title.Text.Strings = (
      'HISTORY')
    OnClickSeries = ChartClickSeries
    BackColor = 7436151
    BottomAxis.Automatic = False
    BottomAxis.AutomaticMaximum = False
    BottomAxis.AutomaticMinimum = False
    BottomAxis.Axis.Color = clGray
    BottomAxis.Axis.Width = 1
    BottomAxis.AxisValuesFormat = '0'
    BottomAxis.ExactDateTime = False
    BottomAxis.Increment = 1.000000000000000000
    BottomAxis.LabelsAngle = 90
    BottomAxis.LabelsFont.Charset = ANSI_CHARSET
    BottomAxis.LabelsFont.Color = clWhite
    BottomAxis.LabelsFont.Height = -11
    BottomAxis.LabelsFont.Name = 'Arial'
    BottomAxis.LabelsFont.Style = []
    BottomAxis.LabelsMultiLine = True
    BottomAxis.LabelsSeparation = 1
    BottomAxis.LabelsSize = 66
    BottomAxis.LabelStyle = talValue
    BottomAxis.Maximum = 7.000000000000000000
    BottomAxis.Minimum = 1.000000000000000000
    BottomAxis.MinorTickCount = 0
    BottomAxis.MinorTickLength = 0
    BottomAxis.StartPosition = 5.000000000000000000
    BottomAxis.EndPosition = 95.000000000000000000
    Frame.Color = clGray
    LeftAxis.Automatic = False
    LeftAxis.AutomaticMaximum = False
    LeftAxis.AutomaticMinimum = False
    LeftAxis.Axis.Color = clGray
    LeftAxis.Axis.Width = 1
    LeftAxis.ExactDateTime = False
    LeftAxis.Increment = 1.000000000000000000
    LeftAxis.LabelsFont.Charset = DEFAULT_CHARSET
    LeftAxis.LabelsFont.Color = clWhite
    LeftAxis.LabelsFont.Height = -11
    LeftAxis.LabelsFont.Name = 'Arial'
    LeftAxis.LabelsFont.Style = []
    LeftAxis.LabelsMultiLine = True
    LeftAxis.Maximum = 5.000000000000000000
    LeftAxis.MinorTickCount = 0
    LeftAxis.MinorTickLength = 0
    LeftAxis.StartPosition = 10.000000000000000000
    LeftAxis.EndPosition = 90.000000000000000000
    LeftAxis.RoundFirstLabel = False
    Legend.Visible = False
    MaxPointsPerPage = 8
    ScaleLastPage = False
    View3D = False
    View3DWalls = False
    BevelOuter = bvNone
    Color = 7436151
    TabOrder = 0
    object Series_BgLine: TLineSeries
      Marks.ArrowLength = 8
      Marks.Visible = False
      SeriesColor = clWhite
      Title = 'Series_BgLine'
      Pointer.InflateMargins = True
      Pointer.Style = psRectangle
      Pointer.Visible = False
      XValues.DateTime = False
      XValues.Name = 'X'
      XValues.Multiplier = 1.000000000000000000
      XValues.Order = loAscending
      YValues.DateTime = False
      YValues.Name = 'Y'
      YValues.Multiplier = 1.000000000000000000
      YValues.Order = loNone
    end
    object Series_Points: TPointSeries
      Cursor = crHandPoint
      Marks.ArrowLength = 0
      Marks.Visible = False
      SeriesColor = 4227327
      Title = 'Series_Points'
      Pointer.HorizSize = 7
      Pointer.InflateMargins = True
      Pointer.Pen.Color = clRed
      Pointer.Pen.Visible = False
      Pointer.Style = psCircle
      Pointer.VertSize = 7
      Pointer.Visible = True
      XValues.DateTime = False
      XValues.Name = 'X'
      XValues.Multiplier = 1.000000000000000000
      XValues.Order = loAscending
      YValues.DateTime = False
      YValues.Name = 'Y'
      YValues.Multiplier = 1.000000000000000000
      YValues.Order = loNone
    end
    object Series_SelectedPoint: TPointSeries
      Marks.ArrowLength = 0
      Marks.Visible = False
      SeriesColor = clMaroon
      Title = 'Series_SelectedPoint'
      Pointer.HorizSize = 11
      Pointer.InflateMargins = True
      Pointer.Pen.Color = clRed
      Pointer.Pen.Visible = False
      Pointer.Style = psCircle
      Pointer.VertSize = 11
      Pointer.Visible = True
      XValues.DateTime = False
      XValues.Name = 'X'
      XValues.Multiplier = 1.000000000000000000
      XValues.Order = loAscending
      YValues.DateTime = False
      YValues.Name = 'Y'
      YValues.Multiplier = 1.000000000000000000
      YValues.Order = loNone
    end
  end
  object Button2p: TButton
    Left = 24
    Top = 298
    Width = 99
    Height = 25
    Caption = 'Button 2 points'
    TabOrder = 1
    OnClick = Button2pClick
  end
  object Button3p: TButton
    Left = 129
    Top = 297
    Width = 105
    Height = 25
    Caption = 'Button 3 Points'
    TabOrder = 2
    OnClick = Button2pClick
  end
  object cbSleepAfterUpdate: TCheckBox
    Left = 416
    Top = 304
    Width = 121
    Height = 17
    Caption = 'Sleep after the update'
    TabOrder = 3
  end
end
0

There are 0 best solutions below