Sign in Join
Wiki copytemporary

Anchor Sides: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11There are some new [[Property|properties]] and [[Method|methods]] for automatic layout of controls. You can now setup controls to keep a certain distance to other controls, or center relative to other controls. See below for examples.
22
33Each of the four sides of a control (Left, Top, Right, Bottom) can now be anchored/bound to a side of another control. For example you can now anchor the left side of [[TEdit]] to the right side of a [[TLabel]]. Everytime the Label is moved or resized the Edit's left side will follow, which normally results in moving the Edit parallel to the Label.
44
55## Example 1
66
7-`+--------+ +-------+`\
8-`| Label1 | | Edit1 |`\
9-`+--------+ |       |`\
10-`           +-------+`
7+```
8++--------+ +-------+
9+| Label1 | | Edit1 |
10++--------+ | |
11+ +-------+
12+```
1113
1214### In code
1315
1416``` Pascal
1517Edit1.AnchorSide[akLeft].Side := asrRight;
1618Edit1.AnchorSide[akLeft].Control := Label1;
1719Edit1.Anchors := Edit1.Anchors + [akLeft];
1820```
1921
2022You can define the distance with the BorderSpacing properties:
2123
2224``` Pascal
2325Edit1.BorderSpacing.Left := 10;
2426```
2527
2628The same can be done with the method:
2729
2830``` Pascal
2931Edit1.AnchorToNeighbour(akLeft, 10, Label1);
3032```
3133
3234### Notes
3335
3436The Edit1.Left will follow Label1.Left+Label1.Width, not the other way around. That means, moving Label1 will move Edit1. But moving Edit1 will be undone by the [[LCL]]. If you also anchor the right side of Label1 to the left side of Edit1, you created a circle, and this can result together with some other autosize properties in a loop. This is ignored by the LCL or automagically repaired when the Parent.AutoSize is [[True|true]].
3537
3638### Via the Anchor Editor
3739
3840The anchor editor is a floating window that is available via the menu **View / Anchor Editor** or via the button on the **Anchors** property in the object inspector.
3941
4042![](/wiki-images/2/28/Anchor_Editor_en.png)
4143
4244## Example 2
4345
4446You can anchor the Edit's top side to follow the Label's top side:
4547
4648 +--------+ +-------+
4749 | Label1 | | Edit1 |
4850 +--------+ | |
4951 +-------+
5052
5153``` Pascal
5254Edit1.AnchorSide[akTop].Side := asrTop;
5355Edit1.AnchorSide[akTop].Control := Label1;
5456Edit1.Anchors := Edit1.Anchors + [akTop];
5557```
5658
5759The same can be done with the method:
5860
5961``` Pascal
6062Edit1.AnchorParallel(akTop,0,Label1);
6163```
6264
6365## Example 3
6466
6567Centering a Label vertically to an Edit:
6668
6769 +-------+
6870 +--------+ | |
6971 | Label1 | | Edit1 |
7072 +--------+ | |
7173 +-------+
7274
7375``` Pascal
7476Edit1.AnchorSide[akTop].Side := asrCenter;
7577Edit1.AnchorSide[akTop].Control := Label1;
7678Edit1.Anchors := Edit1.Anchors + [akTop] - [akBottom];
7779```
7880
7981The same can be done with the method:
8082
8183``` Pascal
8284Edit1.AnchorVerticalCenterTo(Label1);
8385```
8486
8587Obviously anchoring the bottom side of Edit1 does not make sense when centering.
8688
8789## Example 4
8890
8991Using a [[TSplitter]] with the AnchorEditor between two panels: the left panel should behave as if it had `Align=alLeft`, the right panel should behave as if it had `Align=alClient`.
9092
9193- Do not use the `Align` properties of the panels and of the splitter. They must be set to `alNone`. This is important for the splitter for which the default value is `alLeft`.
9294- In the anchor editor do the following adjustments:
9395 - Anchor the top and bottom sides of the panels and the splitter to the corresponding sides of the form.
9496 - Select the first panel and anchor its left side to the left side of the form and its right side to the left side of the splitter.
9597 - Select the second panel and anchor its right side to the right side of the form and its left side to the right side of the splitter.
9698 - The left and right sides of the splitter must not be anchored (as seen from the splitter itself) because this would fix the position of the splitter and make it not movable.
9799
98100The same effect can be achieved by this code:
99101
100102``` pascal
101103procedure TForm1.FormCreate(Sender: TObject);
102104begin
103105 with Panel1 do
104106 begin
105107 AnchorSideTop.Control := Self;
106108 AnchorSideBottom.Control := Self;
107109 AnchorSideBottom.Side := asrBottom;
108110 AnchorSideLeft.Control := Self;
109111 AnchorSideRight.Control := Splitter1;
110112 Anchors := [akLeft, akRight, akTop, akBottom];
111113 end;
112114 with Panel2 do
113115 begin
114116 AnchorSideTop.Control := Self;
115117 AnchorSideBottom.Control := Self;
116118 AnchorSideBottom.Side := asrBottom;
117119 AnchorSideLeft.Control := Splitter1;
118120 AnchorSideLeft.Side := asrBottom;
119121 AnchorSideRight.Control := self;
120122 AnchorSideRight.Side := asrBottom;
121123 Anchors := [akLeft, akRight, akTop, akBottom];
122124 end;
123125 with Splitter1 do
124126 begin
125127 AnchorSideTop.Control := Self;
126128 Splitter1.AnchorSideBottom.Control := Self;
127129 AnchorSideBottom.Side := asrBottom;
128130 Anchors := [akTop, akBottom];
129131 end;
130132end;
131133```
132134
133135## New property
134136
135137``` Pascal
136138property AnchorSide[Kind: TAnchorKind]: TAnchorSide read GetAnchorSide;
137139```
138140
139141This is not published in the [[IDE Window: Object Inspector|object inspector]]. You can edit it in the designer via the [[IDE Window: Anchor Editor|anchor editor]] (Menu: View -\> View anchor editor, or click on button of 'Anchors' property).
140142
141143## New methods to easily configure common layouts
142144
143145``` Pascal
144146procedure AnchorToNeighbour(Side: TAnchorKind; Space: integer; Sibling: TControl);
145147procedure AnchorParallel(Side: TAnchorKind; Space: integer; Sibling: TControl);
146148procedure AnchorHorizontalCenterTo(Sibling: TControl);
147149procedure AnchorVerticalCenterTo(Sibling: TControl);
148150procedure AnchorAsAlign(TheAlign: TAlign; Space: Integer);
149151```
150152
151153AnchorVerticalCenterTo works with Parent too. Then it will center on the client area, that means the center of the control is at *ClientHeight div 2*.
152154
153155Center anchoring is not yet fully supported when computing the size of the parent. For example when you put a label center anchored into a [[TGroupBox|Groupbox1]] and set Groupbox1.AutoSize to true then Groupbox1 height will shrink leaving no space for the label. The solution is to center to a control that is not centered. For example center a Label1 to a [[TComboBox|ComboBox]] and use for the ComboBox the default anchors (Anchors=\[akLeft,akTop\]).
154156
155157## Anchoring to invisible controls
156158
157159Anchoring to invisible controls works intuitively. For example: The below controls A, B, C are anchored (C.Left to B.Right and B.Left to A.Right):
158160
159-` +---+ +---+ +---+`\
160-` | A | | B | | C |`\
161-` +---+ +---+ +---+`
161+```
162+ +---+ +---+ +---+
163+ | A | | B | | C |
164+ +---+ +---+ +---+
165+```
162166
163167If B is hidden (Visible:=[[False|false]]) then C.Left will skip B and use the right of A, resulting in
164168
165-` +---+ +---+`\
166-` | A | | C |`\
167-` +---+ +---+`
169+```
170+ +---+ +---+
171+ | A | | C |
172+ +---+ +---+
173+```
168174
169175## Circular references
170176
171177You can build circular references by anchoring two sides to each other, which creates an impossible anchoring. The LCL notices that, but will not raise an exception, because it can be a temporary circle. For example: A row of buttons. Button1 is left of Button2, Button2 is left of Button3. Now the order is changed to: Button3, Button1, Button2. If the reanchoring is started with Button3, a circle is created temporarily and is fixed when the reordering is completed. The LCL detects circles and will not move the Button. Circles are not the only anomaly.
172178
173179There is one exception to this rule. If the Parent.AutoSize is true, then the LCL will automatically break circles on autosize and writes a warning via [[DebugLn|debugln]] (windows: --debug-log.txt, linux, et al: stdout). Which anchor is broken depends on the order of controls and the sides. So, temporary circles are still allowed with AutoSize=true if you enclose the change in
174180
175181``` Pascal
176182Parent.DisableAutosizing;
177183try
178184 // change anchors, aligns, bounds...
179185finally
180186 Parent.EnableAutosizing;
181187end;
182188```
183189
184190The AutoSize algorithm does not only break circles, but fixes Align/AnchorSide inconsistencies too.
185191
186192Eventually it would be good to add some hints in the designer or a tool to list the circles and inconsistencies.
187193
188194## See also
189195
190196- [[Autosize / Layout]]
191197- [[Example: Anchors. How to reliably align dynamically created controls under changing visibility]]
192198
193199---
194200*From the Free Pascal wiki, page "Anchor Sides", as it stood on 2021-10-11 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history