Recent

Author Topic: [SOLVED] Anchoring dynamically created controls on a TPage  (Read 488 times)

guest63715

  • Guest
[SOLVED] Anchoring dynamically created controls on a TPage
« on: March 01, 2019, 04:53:54 pm »
How do you properly anchor dynamically created components on a TPage?

When I add controls to a page using the designer the anchors work correctly but when I do it at runtime it doesn't work as expected.

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Notebook1: TNotebook;
  16.     Page1: TPage;
  17.     procedure FormCreate(Sender: TObject);
  18.   end;
  19.  
  20. var
  21.   Form1: TForm1;
  22.  
  23. implementation
  24.  
  25. {$R *.lfm}
  26.  
  27. { TForm1 }
  28.  
  29. procedure TForm1.FormCreate(Sender: TObject);
  30. var
  31.   button: TButton;
  32. begin
  33.   Notebook1.Anchors := [akTop, akLeft, akRight];
  34.  
  35.   button := TButton.Create(Self);
  36.   with button do
  37.   begin
  38.     Anchors := [akTop, akRight];
  39.     Left := 8;
  40.     Name := 'Button1';
  41.     Parent := Page1;
  42.     Top := 8
  43.   end
  44. end;
  45.  
  46. end.
  47.  

lucamar

  • Hero Member
  • *****
  • Posts: 1299
Re: Anchoring dynamically created controls on a TPage
« Reply #1 on: March 01, 2019, 05:25:28 pm »
Not related to your question, but ...

Are you aware that because it's local to the procedure your button is left in limbo when FormCreate() exits? The "normal" way of adding dynamic controls is declaring them in the form's class. Like this:
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Notebook1: TNotebook;
  16.     Page1: TPage;
  17.     procedure FormCreate(Sender: TObject);
  18.   public
  19.     AButton:TButton;
  20.   end;
  21.  
  22. var
  23.   Form1: TForm1;
  24.  
  25. implementation
  26.  
  27. {$R *.lfm}
  28.  
  29. { TForm1 }
  30.  
  31. procedure TForm1.FormCreate(Sender: TObject);
  32. begin
  33.   Notebook1.Anchors := [akTop, akLeft, akRight];
  34.  
  35.   AButton := TButton.Create(Self);
  36.   with AButton do
  37.   begin
  38.     Anchors := [akTop, akRight];
  39.     Left := 8;
  40.     Name := 'Button1';
  41.     Parent := Page1;
  42.     Top := 8
  43.   end
  44. end;
  45.  
  46. end.

Alternatively, you may just do:
Code: Pascal  [Select]
  1.   with TButton.Create(Self) do
  2.   begin
  3.     Left := 8;
  4.     Top := 8
  5.     Name := 'Button1';
  6.     Parent := Page1;
  7.     Anchors := [akTop, akRight];
  8.   end;
without declaring/using any TButton field/variable.

Re. your question, try setting the anchors after setting the parent and other related properties (bounds, etc.) like I did in the second example. I'm not sure that's the answer but it's worth a try.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 1.8.4 & 2.0.2 w/FPC 3.0.4 on:
(K|L)Ubuntu 12..16, Windows XP SP3, various DOSes.

howardpc

  • Hero Member
  • *****
  • Posts: 2968
Re: Anchoring dynamically created controls on a TPage
« Reply #2 on: March 01, 2019, 05:33:10 pm »
As well as specifying the anchors you want (akTop, etc.) you have to specify the properties of each anchor.
You can rely on the AnchorSide property always having a default value, except it may not be the value you need for your layout.
In a new Lazarus project, double-click the empty form to generate an OnCreate handler, and add code as follows (don't drop any components on the form).

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Forms, Controls, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   TForm1 = class(TForm)
  13.     procedure FormCreate(Sender: TObject);
  14.   private
  15.     Notebook: TNotebook;
  16.     Button: TButton;
  17.     procedure Button1Click(Sender: TObject);
  18.   end;
  19.  
  20. var
  21.   Form1: TForm1;
  22.  
  23. implementation
  24.  
  25. {$R *.lfm}
  26.  
  27. procedure TForm1.FormCreate(Sender: TObject);
  28. begin
  29.   Autosize := False;
  30.  
  31.   Notebook := TNotebook.Create(Self);
  32.   with Notebook do
  33.     begin
  34.       BorderSpacing.Around := 10;
  35.       Anchors := [akTop, akLeft, akRight];
  36.       AnchorSideTop.Control := Self;
  37.       AnchorSideLeft.Control := Self;
  38.       AnchorSideRight.Control := Self;
  39.       AnchorSideRight.Side := asrRight;
  40.       Parent := Self;
  41.     end;
  42.  
  43.   Notebook.Pages.Add('Page 1');
  44.  
  45.   Button := TButton.Create(Notebook);
  46.   with Button do
  47.     begin
  48.       Anchors := [akTop, akRight];
  49.       AnchorSideTop.Control := Notebook.Page[0];
  50.       AnchorSideRight.Control := Notebook.Page[0];
  51.       AnchorSideRight.Side := asrRight;
  52.       BorderSpacing.Around := 10;
  53.       Name := 'Button1';
  54.       Caption := 'Button 1';
  55.       OnClick := @Button1Click;
  56.       Parent := Notebook.Page[0];
  57.     end;
  58. end;
  59.  
  60. procedure TForm1.Button1Click(Sender: TObject);
  61. begin
  62.   Caption := (Sender as TButton).Name + ' was clicked';
  63. end;
  64.  
  65. end.
« Last Edit: March 01, 2019, 05:36:31 pm by howardpc »

garlar27

  • Hero Member
  • *****
  • Posts: 582
Re: Anchoring dynamically created controls on a TPage
« Reply #3 on: March 01, 2019, 06:46:58 pm »
I did a few procedures to help me on that :
Code: Pascal  [Select]
  1. {___________________________________________________________________________________________________________________________ CreateControl }
  2. function CreateControl(var ACtrl: TControl; const ACtrlClass: TControlClass; const TheOwner: TControl; const TheParent: TWinControl;
  3.    const ALeft, ATop, AWidth, AHeight: Integer): Boolean;
  4. begin
  5.    Result := CreateControl(ACtrl, ACtrlClass, TheOwner, TheParent);
  6.  
  7.    ACtrl.Top    := ATop   ;
  8.    ACtrl.Left   := ALeft  ;
  9.    ACtrl.Width  := AWidth ;
  10.    ACtrl.Height := AHeight;
  11. end; {<--- CreateControl }
  12.  
  13.  
  14.  
  15. {_______________________________________________________________________________________________________________________ SetCtrlAnchorSide }
  16. procedure SetCtrlAnchorSide(const TheCtrl: TControl; const CtrlSide: TAnchorKind; const ACtrl: TControl;
  17.    const AnchrMode: TAnchorSideReference; const AMargin: Integer);
  18. begin
  19.    if not Assigned(TheCtrl) or not (TheCtrl.Align in [alNone, alCustom]) then begin
  20.       Exit;
  21.    end;
  22.  
  23.    case CtrlSide of
  24.       akTop   : begin TheCtrl.BorderSpacing.Top    := AMargin; end;
  25.       akLeft  : begin TheCtrl.BorderSpacing.Left   := AMargin; end;
  26.       akRight : begin TheCtrl.BorderSpacing.Right  := AMargin; end;
  27.       akBottom: begin TheCtrl.BorderSpacing.Bottom := AMargin; end;
  28.    end; //<---  del case CtrlSide  //
  29.  
  30.    TheCtrl.Anchors := TheCtrl.Anchors + [CtrlSide];
  31.  
  32.    TheCtrl.AnchorSide[CtrlSide].Control := ACtrl    ;
  33.    TheCtrl.AnchorSide[CtrlSide].Side    := AnchrMode;
  34. end; {<--- SetCtrlAnchorSide }
  35.  
  36.  
  37.  
  38. {___________________________________________________________________________________________________________________________ SetCtrlLayout }
  39. procedure SetCtrlLayout(const TheCtrl: TControl; const CtrlAlign: TAlign; const MargAround, MargInner: Integer; const TheAnchors: TAnchors;
  40.    const TopCtrl: TControl; const TopAnchrMode: TAnchorSideReference; const TopMargin: Integer; const LeftCtrl: TControl;
  41.    const LeftAnchrMode: TAnchorSideReference; const LeftMargin: Integer; const BottomCtrl: TControl;
  42.    const BottomAnchrMode: TAnchorSideReference; const BottomMargin: Integer; const RightCtrl: TControl;
  43.    const RightAnchrMode: TAnchorSideReference; const RightMargin: Integer);
  44. begin
  45.    if not Assigned(TheCtrl) then begin
  46.       Exit;
  47.    end;
  48.  
  49.    TheCtrl.Align  := CtrlAlign;
  50.  
  51.    TheCtrl.BorderSpacing.Around      := MargAround  ;
  52.    TheCtrl.BorderSpacing.InnerBorder := MargInner   ;
  53.    TheCtrl.BorderSpacing.Top         := TopMargin   ;
  54.    TheCtrl.BorderSpacing.Left        := LeftMargin  ;
  55.    TheCtrl.BorderSpacing.Bottom      := BottomMargin;
  56.    TheCtrl.BorderSpacing.Right       := RightMargin ;
  57.  
  58.    if not (TheCtrl.Align in [alNone, alCustom]) then begin//TheCtrl.Align <> alNone then begin
  59.       Exit;
  60.    end;
  61.  
  62.    TheCtrl.Anchors := TheAnchors;
  63.  
  64.    TheCtrl.AnchorSide[akTop   ].Control := TopCtrl        ;
  65.    TheCtrl.AnchorSide[akTop   ].Side    := TopAnchrMode   ;
  66.  
  67.    TheCtrl.AnchorSide[akLeft  ].Control := LeftCtrl       ;
  68.    TheCtrl.AnchorSide[akLeft  ].Side    := LeftAnchrMode  ;
  69.  
  70.    TheCtrl.AnchorSide[akBottom].Control := BottomCtrl     ;
  71.    TheCtrl.AnchorSide[akBottom].Side    := BottomAnchrMode;
  72.  
  73.    TheCtrl.AnchorSide[akRight ].Control := RightCtrl      ;
  74.    TheCtrl.AnchorSide[akRight ].Side    := RightAnchrMode ;
  75.  
  76. end; {<--- SetCtrlLayout }
  77.  

and use it this way
Code: Pascal  [Select]
  1.    CreateControl(TControl(FPanel), TPanel, TheOwner, TheOwner);
  2.    FPanel.Align  := alClient;
  3.  
  4.    //   ANCHOR THIS WAY:
  5.    SetCtrlLayout(AShape, alNone, 0, 0, [akLeft, akTop], AForm, asrCenter   , 0, AForm, asrCenter  , 0, nil, asrTop   , 0, nil, asrBottom, 0);
  6.  
  7.    //   OR ANCHOR THIS WAY:
  8.    SetCtrlAnchorSide(AShape, akLeft  , AForm, asrCenter   , 0);
  9.    SetCtrlAnchorSide(AShape, akRight , AForm, asrCenter   , 0);
  10.