Centered Marks on Stacked Bars

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
Post Reply
strobbekoen
Newbie
Newbie
Posts: 23
Joined: Tue Feb 10, 2009 12:00 am

Centered Marks on Stacked Bars

Post by strobbekoen » Thu Sep 13, 2012 11:02 am

Hi,

I have a application where I show volume distributions using stacked bars, as in the attached screenshot.
I am displaying the volumes using the Marks but I am having some difficulty getting them in a readable position.
Ideally I would like to have the Marks centered on each bar value.
Is there any way to achieve this ?

Thank you.
Attachments
iqo2_cal1.gif
iqo2_cal1.gif (24.18 KiB) Viewed 3131 times

Yeray
Site Admin
Site Admin
Posts: 8724
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Centered Marks on Stacked Bars

Post by Yeray » Thu Sep 13, 2012 11:29 am

Hi,

The MarksOnBar property was introduced to do this. Setting it to true and combining it with with MarksLocation to mlCenter should do it.
But I'm afraid this new feature doesn't work fine with Stacked Bars. This is a know issue already in the wish list to be implemented in future releases (TV52015085).

In the meanwhile you could modify the marks positions manually as here, or just draw the marks manually with custom drawing methods.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

strobbekoen
Newbie
Newbie
Posts: 23
Joined: Tue Feb 10, 2009 12:00 am

Re: Centered Marks on Stacked Bars

Post by strobbekoen » Thu Sep 13, 2012 11:42 am

Hi,

I am using version 8.08 with Delphi 2009. I can't find the MarksOnBar property, but since you say it's not optimal for stacked bars, I think I will go for the custom option.

I tried code like this:

Code: Select all

procedure TFramePeriodicChart.RefreshMarks;
var
  I,J: Integer;
  S: TChartSeries;
begin
  for I := 0 to Chart.SeriesCount - 1 do begin
    S := Chart.Series[I];
    if S.Visible and S.Active and S.Marks.Visible then begin
      if S is TBarSeries then begin
        for J := 0 to S.Marks.Positions.Count - 1 do begin
          with S.Marks.Positions[J] do begin
            Custom := True;
            // calculate LeftTop.Y position
          end;
        end;
      end;
    end;
  end;
end;
I call this procedure after I create and populate all series and after the chart is drawn. For some reason, I still get a access violation (presumable because the marks position objects are not valid).

Alternatively, I may just draw them myself using the afterdraw event. Can i use the Marks.DrawItem method to draw them even if Marks.Visible = False ? Any suggestions how to calculate the center position of stacked bar values ?

Sandra
Site Admin
Site Admin
Posts: 3132
Joined: Fri Nov 07, 2008 12:00 am

Re: Centered Marks on Stacked Bars

Post by Sandra » Thu Sep 13, 2012 3:18 pm

Hello strobbeskoen,

I try your code and the access violation doesn't appear for me. Can you please, attached all project so we can try to reproduce your problem and find a solution for you?

Thanks,
Best Regards,
Sandra Pazos / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

Yeray
Site Admin
Site Admin
Posts: 8724
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Centered Marks on Stacked Bars

Post by Yeray » Thu Sep 13, 2012 3:54 pm

Hi,

I've just modified the sources to implement the request TV52015085. So the MarksOnBar will align the marks correctly also with Stacked bars in the next maintenance release.

Regarding the manually repositioning of the marks, I've tried the following code and it seems to work fine for me here, without access violations:

Code: Select all

uses Series, Types;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
  for i:=0 to 2 do
    with Chart1.AddSeries(TBarSeries) as TBarSeries do
    begin
      FillSampleValues;
      MultiBar:=mbStacked;
      Marks.Arrow.Visible:=false;
      Transparency:=20;
    end;

    RefreshMarks;
end;

procedure TForm1.RefreshMarks;
var
  I,J: Integer;
  S: TChartSeries;
  barBounds: TRect;
  barHeight: Integer;
begin
  Chart1.Draw;

  for I := 0 to Chart1.SeriesCount - 1 do begin
    S := Chart1.Series[I];
    if S.Visible and S.Active and S.Marks.Visible then begin
      if S is TBarSeries then begin
        for J := 0 to S.Marks.Positions.Count - 1 do begin
          with S.Marks.Positions[J] do begin
            Custom := True;
            barBounds:=(S as TBarSeries).CalcBarBounds(J);
            barHeight:=S.CalcYPosValue(0)-S.CalcYPosValue(S.YValue[J]);
            LeftTop.Y:=barBounds.Top+(barHeight div 2);
            LeftTop.X:=S.CalcXPos(J);
          end;
        end;
      end;
    end;
  end;
end;


procedure TForm1.Chart1Scroll(Sender: TObject);
begin
  RefreshMarks;
end;

procedure TForm1.Chart1Zoom(Sender: TObject);
begin
  RefreshMarks;
end;

procedure TForm1.Chart1UndoZoom(Sender: TObject);
begin
  RefreshMarks;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

strobbekoen
Newbie
Newbie
Posts: 23
Joined: Tue Feb 10, 2009 12:00 am

Re: Centered Marks on Stacked Bars

Post by strobbekoen » Fri Sep 14, 2012 10:32 am

Hi,

I got a access violation because there are also null points so the mark position object can be nil.

I have a central procedure called RefreshChart. All the series are created at runtime depending on the settings the user selects.
The very first time when the form is created and the chart is refreshed, the marks still appeared in the old position so I added a Invalidate to refresh it.
After that, I used chart after draw and check for the flag if it's refreshed and then call RefreshMarks.

I also accounted for the height of the mark itself to position it.
Wouldn't barHeight always be the same as barBounds.Bottom - barBounds.Top ?

Seems to work okay now, apart from the shoehorn to refresh it the very first time. :)

Code: Select all

procedure TFramePeriodicChart.RefreshChart;
begin
  Inc(FRefreshing);
  try
    FRefreshed := False;
   // create series and refresh other internal stuff ...
    Chart.Draw;
    RefreshMarks;
    Chart.Invalidate;
    FRefreshed := True;
  finally
    Dec(FRefreshing);
  end;
end;

procedure TFramePeriodicChart.RefreshMarks;
var
  I,J: Integer;
  S: TChartSeries;
  P: TSeriesMarkPosition;
  barBounds: TRect;
  barHeight: Integer;
  // Y: Integer;
begin
  for I := 0 to Chart.SeriesCount - 1 do begin
    S := Chart.Series[I];
    if S.Visible and S.Active and S.Marks.Visible then begin
      if S is TBarSeries then begin
        for J := 0 to S.Marks.Positions.Count - 1 do begin
          P := S.Marks.Positions[J];
          if P <> nil then begin
            P.Custom := True;
            barBounds := TBarSeries(S).CalcBarBounds(J);
            barHeight := S.CalcYPosValue(0) - S.CalcYPosValue(S.YValue[J]);
            P.LeftTop.Y := barBounds.Top + ((barHeight-P.Height) div 2);
            // P.LeftTop.X := S.CalcXPos(J);
          end;
        end;
      end;
    end;
  end;
end;

procedure TFramePeriodicChart.ChartAfterDraw(Sender: TObject);
begin
  if FRefreshed then
    RefreshMarks;
end;
Attachments
iqo2_cal2.gif
iqo2_cal2.gif (25.46 KiB) Viewed 3007 times

Yeray
Site Admin
Site Admin
Posts: 8724
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Centered Marks on Stacked Bars

Post by Yeray » Fri Sep 14, 2012 11:15 am

Hi,
strobbekoen wrote:Wouldn't barHeight always be the same as barBounds.Bottom - barBounds.Top ?
Oups, you are right.

I'm glad to hear it works fine for you now! :D
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Post Reply