Reron
Reron

Reputation: 191

Teechart, automatic recalculation of a custom Mark Area due to title changes

Delphi 10.1, VCL, with embedded Teechart.

I've an Area Series with a Mark moved by code to a custom position. When the Mark title content is changed, the Yellow background is not adjusted (Auto Sized) to the new Mark content. I've a work around for that, but it has flickers and it is not elegant. I'm looking for an idea how to do it better.

In details: I put three buttons on the chart, one to move the Mark location, the 2nd button to add a second content line to Mark title. The 3rd button is my work around to get a proper size. The Series Creation:

procedure TForm2.FormCreate(Sender: TObject);
begin
  Chart1.View3D := false;
  Chart1.Axes.Bottom.SetMinMax(0,5);

  with Chart1.AddSeries(tAreaSeries) as tAreaSeries do
    begin
      AddXY(1, 10);                  // Two points AreaSeries
      AddXY(4, 10);
      Marks[1].Visible     := false; // Hide the other Mark, the default is true
      Marks.Visible        := true;  // Global Visibility for all Markers
      Chart1[0].Marks[0].Text.Text := 'First-line';
    end;
end;

Pressing Move Mark button code:

procedure TForm2.btnMoveMarkClick(Sender: TObject);
begin
  Chart1[0].Marks.Positions[0].Custom := true;
  Chart1[0].Marks.Positions[0].Offset(point(50,70));
//  Chart1[0].Marks.Positions[0].LeftTop := point(150,200);  // It is moving the Mark but not drawing the line to Series point
  Chart1.Repaint; // It doesn't work without this Repaint
end;

Will generate the following screen: enter image description here Now, pressing the 2nd button to change Mark Title content as follow:

procedure TForm2.btnChangeMarkContentClick(Sender: TObject);
begin
  Chart1[0].Marks[0].Text.Text := 'First-line'+#13+'Second-line';
end;

As you can see, the Yellow background size was not changed: enter image description here

My brut-force work around is to delete the custom position, this will resize the Mark, then reposition the Mark again as follow:

procedure TForm2.btnResizeMarkClick(Sender: TObject);
var
  LastPoint: tpoint;
begin
  LastPoint := Chart1[0].Marks.Positions[0].LeftTop;
  Chart1[0].Marks.Positions.Automatic(0);
  Chart1.Repaint;

  Chart1[0].Marks.Positions[0].Custom := true;
  Chart1.Repaint;
//  Chart1[0].Marks[0].MoveTo(LastPoint); // It doesn't work - Why?
  Chart1[0].Marks.Positions[0].LeftTop := LastPoint; // Better to use Offset
  Chart1.Repaint;
end;

It is doing the job, but with flicker due to Mark movement, as follow: enter image description here

Thanks for any hint how to resize the Mark without delete its custom position which cause the flicker. Reron

Upvotes: 0

Views: 527

Answers (2)

Reron
Reron

Reputation: 191

Yeray solved the main problem. In addition, the Arrows should be adjusted too as follow:

type
  tCustomTextShapeAccess = class(tCustomTextShape); // Yeray: tCustomTextShapeAccess class to get access to the protected CalcBounds method

const
  tcaTopLeft = 0;
  tcaArrowTo = 1;

procedure TeeChart_ResizeCustomMark(aChart: tChart; aSeriesInx, aMarkInx, aAnchor: integer);
// Resize Custom Mark area shape. It is required after Title text modification
// aAnchor: tcaTopLeft(0), tcaArrowTo(1); Choose which point to keep
var
  aSeries: tChartSeries;
  aMark  : tMarksItem;
  aMarkPosision: tSeriesMarkPosition;
begin
  // Assignments for more readable code
  aSeries       := aChart[aSeriesInx];
  aMark         := aChart[aSeriesInx].Marks[aMarkInx];
  aMarkPosision := aSeries.Marks.Positions[aMarkInx];

  // Bounds Calculation of the new Mark. Yeray solution.
  tCustomTextShapeAccess(aMark).CalcBounds(aChart); // Yeray: tCustomTextShapeAccess class to get access to the protected CalcBounds method
  aMarkPosision.Height := aMark.Height;
  aMarkPosision.Width  := aMark.Width;

  // Set Mark position based on aAnchor
  case aAnchor of
    tcaTopLeft: // Keep LeftTop point. Set new ArrowTo point.
      begin
        aMarkPosision.ArrowTo.X := aMarkPosision.LeftTop.X + (aMarkPosision.Width div 2);
        if aSeries.CalcYPos(aMarkInx) > aMarkPosision.ArrowTo.Y then // Mark above Series point
          aMarkPosision.ArrowTo.Y := aMarkPosision.LeftTop.Y + aMarkPosision.Height
        else
          aMarkPosision.ArrowTo.Y := aMarkPosision.LeftTop.Y;
      end;
    else        // Set ArrowTo point. Set a New LeftTop point.
      begin
        aMarkPosision.LeftTop.X := aMarkPosision.ArrowTo.X - (aMarkPosision.Width div 2);
        if aSeries.CalcYPos(aMarkInx) > aMarkPosision.ArrowTo.Y then // Mark above Series point
          aMarkPosision.LeftTop.Y := aMarkPosision.ArrowTo.Y - (aMarkPosision.Height -1)
        else                                                         // Mark below Series point
          aMarkPosision.ArrowTo.Y := aMarkPosision.LeftTop.Y;
      end;
  end; // case

  aChart.Repaint;
end;

Upvotes: 0

Yeray
Yeray

Reputation: 5039

You could recalculate the Mark bounds and assign the Width and Height to the corresponding Position:

  TCustomTextShapeAccess(Chart1[0].Marks[0]).CalcBounds(Chart1);
  Chart1[0].Marks.Positions[0].Height:=Chart1[0].Marks[0].Height;
  Chart1[0].Marks.Positions[0].Width:=Chart1[0].Marks[0].Width;
  Chart1.Repaint;

Note you have to declare the TCustomTextShapeAccess class to get access to the protected CalcBounds method:

type TCustomTextShapeAccess=class(TCustomTextShape);

Upvotes: 1

Related Questions