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!