Page 1 of 1

Centered Marks on Stacked Bars

Posted: Thu Sep 13, 2012 11:02 am
by 10051902
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.

Re: Centered Marks on Stacked Bars

Posted: Thu Sep 13, 2012 11:29 am
by yeray
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.

Re: Centered Marks on Stacked Bars

Posted: Thu Sep 13, 2012 11:42 am
by 10051902
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 ?

Re: Centered Marks on Stacked Bars

Posted: Thu Sep 13, 2012 3:18 pm
by 10050769
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,

Re: Centered Marks on Stacked Bars

Posted: Thu Sep 13, 2012 3:54 pm
by yeray
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;

Re: Centered Marks on Stacked Bars

Posted: Fri Sep 14, 2012 10:32 am
by 10051902
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;

Re: Centered Marks on Stacked Bars

Posted: Fri Sep 14, 2012 11:15 am
by yeray
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