Legend positioning / sizing

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
Post Reply
Sam F
Newbie
Newbie
Posts: 45
Joined: Wed Sep 10, 2008 12:00 am

Legend positioning / sizing

Post by Sam F » Fri Nov 25, 2011 2:32 am

Hi

I'm trying to position a legend on the right. I've tried setting the alignment property so that I can specify via just setting the rightmost edge, but that doesn't seem to work. Is this a bug?

So instead I've been trying to do something like:
Chart1->Legend->Left = Chart1->Width - Chart1->Legend->Width - 2;

Unfortunately, the reported Chart1->Legend->Width is frequently erroneous, so the legend can shift all over the place, despite not necessarily changing size. To reproduce the erroneous width reporting, it seems related to toggling either series visibility, or the ShowInLegend property of a series (which can change the legend width, but it is still reported incorrectly).

See attached picture. I produced this by :

Code: Select all

__fastcall TFormMain::TFormMain(TComponent* Owner)
  : TForm(Owner)
{
  Chart1->AddSeries(new TLineSeries(Chart1));
  Chart1->Series[0]->AddXY(1,1);
  Chart1->Series[0]->AddXY(2,2);
  Chart1->AddSeries(new TLineSeries(Chart1));
  Chart1->Series[1]->AddXY(3,3);
  Chart1->Series[1]->AddXY(4,4);
  Chart1->Legend->Visible = true;

  Label1->Caption = "";
  Toggler = false;
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::TScreenUpdateTimer(TObject *Sender)
{
  Toggler = !Toggler;

  Chart1->Series[0]->Visible = Toggler;

  //Use legend width to position the legend with respect to the right boundary
  Chart1->Legend->Left = Chart1->Width - Chart1->Legend->Width - 2;

  AnsiString Message = Label1->Caption;
  Message += AnsiString(_T("Reported Legend Width = ")) + AnsiString(Chart1->Legend->Width) + _T("\n");
  Label1->Caption = Message;
}
So, I'm struggling just to position the legend. I'd be willing to use just a work around at this point. Is there an event perhaps where, by then, the width will be correctly reported?
Attachments
ErroneousLegendWidth.png
ErroneousLegendWidth.png (45.97 KiB) Viewed 15832 times

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

Re: Legend positioning / sizing

Post by Yeray » Fri Nov 25, 2011 3:31 pm

Hi Sam,

Take care when you change some properties in the chart and you retrieve others "immediately" afterwards, because some properties of the chart aren't updated until the charts is repainted. So in this cases I'd suggest you to try forcing some chart repaints to see if this affects the behaviour.
And I think that's the case here. Calling Chart1->Draw() before using Chart1->Legend->Left it seems to work fine for me here.
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

Sam F
Newbie
Newbie
Posts: 45
Joined: Wed Sep 10, 2008 12:00 am

Re: Legend positioning / sizing

Post by Sam F » Mon Nov 28, 2011 5:45 am

Keep in mind that whatever manner it is calculating the legend width, it seems to breakable - it isn't actually '-12' at any point in time - in the example I used, it is flicking between displaying the information for one or two series.
Perhaps it needs to resort to some cache value if certain other values are invalid for it to use in the width calculation, or if it is halfway through an update.
But yes, I'll try your solution.

Sam F
Newbie
Newbie
Posts: 45
Joined: Wed Sep 10, 2008 12:00 am

Re: Legend positioning / sizing

Post by Sam F » Tue Nov 29, 2011 6:01 am

So, I find if I set the left property, it effects the width property. Which is unfortunate, because I use the width property to set the left property - leading to the obvious potential of a feedback cycle. Calling the chart repaint or the legend repaint does not guarantee a valid width after setting the left. The following code, for example, will report a legend width of 0 in my little test project every time the timer goes off.

Code: Select all

Chart1->Legend->Left = Chart1->Legend->Left + 76;
  Chart1->Repaint();
  Chart1->Legend->Repaint();
  AnsiString Message = Label1->Caption;
  Message += AnsiString(_T("Reported Legend Width = ")) + AnsiString(Chart1->Legend->Width) + _T("\n");
  Label1->Caption = Message;
The problem I have all this with is a real-time updating chart (so is repainting every second) - and hence I'm trying to set the valid legend left every second too, but it appears I don't have a means of determining what the valid left pixel is for a legend that I can't determine the width of.

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

Re: Legend positioning / sizing

Post by Yeray » Thu Dec 01, 2011 12:07 pm

Hello Sam,

Note the Width property returns the difference between Right and Left properties (+1 pixel). Since you modify just one of them, you need to set the other property too or to repaint the chart for the other property to be recalculated.
- To modify the Legend Right property:

Code: Select all

Chart1.Legend.ShapeBounds.Right:=Chart1.Width - 2 - 1;
- To force the chart to be repainted, you have to repaint it after changing the legend items (activating/deactivating a series, 1st repaint) and after modifying the left property (2nd repaint):

Code: Select all

uses Series;

var Toggler: Bool;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1.AddSeries(TLineSeries);
  Chart1[0].AddXY(1,1);
  Chart1[0].AddXY(2,2);
  Chart1.AddSeries(TLineSeries);
  Chart1[1].AddXY(3,3);
  Chart1[1].AddXY(4,4);
  Chart1.Visible:=true;

  Label1.Caption:='';
  Toggler:=false;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Toggler:=not Toggler;

  Chart1[0].Visible:=Toggler;
  Chart1.Repaint; //1st Repaint
  Label1.Caption:=Label1.Caption+'Reported Legend Width Before = ' + IntToStr(Chart1.Legend.Width) + #10#13;
  Chart1.Legend.Left:=Chart1.Width - Chart1.Legend.Width - 2;
  Chart1.Repaint; //2nd Repaint
  Label1.Caption:=Label1.Caption+'Reported Legend Width After = ' + IntToStr(Chart1.Legend.Width) + #10#13;

{  Chart1.Legend.Left:=Chart1.Legend.Left + 76;
  Chart1.Repaint;
  Chart1.Legend.Repaint;
  Label1.Caption:=Label1.Caption+'Reported Legend Width = ' + IntToStr(Chart1.Legend.Width) + #10#13;}
end;
Sam F wrote:The following code, for example, will report a legend width of 0 in my little test project every time the timer goes off.
I'm trying to reproduce it but I can't. Here is the complete code I'm using in Delphi but I see no "0" in the label:

Code: Select all

uses Series;

var Toggler: Bool;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1.AddSeries(TLineSeries);
  Chart1[0].AddXY(1,1);
  Chart1[0].AddXY(2,2);
  Chart1.AddSeries(TLineSeries);
  Chart1[1].AddXY(3,3);
  Chart1[1].AddXY(4,4);
  Chart1.Visible:=true;

  Label1.Caption:='';
  Toggler:=false;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Toggler:=not Toggler;

  Chart1[0].Visible:=Toggler;

  Chart1.Legend.Left:=Chart1.Legend.Left + 76;
  Chart1.Repaint;
  Chart1.Legend.Repaint;
  Label1.Caption:=Label1.Caption+'Reported Legend Width = ' + IntToStr(Chart1.Legend.Width) + #10#13;
end;
PS: Excuse me for using Delphi instead of C++Builder. I don't think you'll find any problem understanding/translating it but if you do, don't hesitate to let me know.
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

Sam F
Newbie
Newbie
Posts: 45
Joined: Wed Sep 10, 2008 12:00 am

Re: Legend positioning / sizing

Post by Sam F » Fri Dec 02, 2011 1:08 am

Thankyou Yeray.

No problems reading delphi. Setting the ShapeBounds.Right property seems to be resolve my problems. I'll use the following code now whenever I want to set a legend left:

Code: Select all

  int LegWidth = Chart1->Legend->Width;
  Chart1->Legend->Left = NewLeft;
  Chart1->Legend->ShapeBounds.Right = Chart1->Legend->Left + LegWidth;
Because I'm using the width for setting the left, it is very important for me to maintain the validity of the width field when setting the left property, which this code seems to accomplish. Thank you!

Sam F
Newbie
Newbie
Posts: 45
Joined: Wed Sep 10, 2008 12:00 am

Re: Legend positioning / sizing

Post by Sam F » Fri Dec 02, 2011 2:31 am

Actually, it hasn't entirely resolved my problem, but it seems to help. For some reason I still get jitter of a few pixels. Rather than chase that down I'm switching to a one-off setting of the legend left at the start of any real time updating session.

Sam F
Newbie
Newbie
Posts: 45
Joined: Wed Sep 10, 2008 12:00 am

Re: Legend positioning / sizing

Post by Sam F » Fri Dec 02, 2011 2:36 am

Ah, sorry, I was missing the -1. Now it works for me with no jitter.

Code: Select all

  int LegWidth = Chart1->Legend->Width;
  Chart1->Legend->Left = NewLeft;
  Chart1->Legend->ShapeBounds.Right = Chart1->Legend->Left + LegWidth - 1;

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

Re: Legend positioning / sizing

Post by Yeray » Fri Dec 02, 2011 10:15 am

Hi Sam,

Great! I'm glad to hear you solved it. :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