Sign in Join
Wiki copytemporary

Autosize / Layout/ru: what changed

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

OldNewLine
11# Введение
22
33LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д.
44
55- Фиксированный дизайн (Fixed design): это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно.
66- [[Autosize / Layout/ru|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.
77- [[Autosize / Layout/ru|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.
88- [[Autosize / Layout/ru|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)
99- [[Autosize / Layout/ru|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.
1010- [[Autosize / Layout/ru|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.
1111
1212## Правила приоритета
1313
14141. Constraints (Ограничения)
15152. Align (Выравнивание)
16163. Anchors(Привязки)
17174. ChildSizing.Layout
18185. AutoSize (Автомасштаб элемента)
19196. События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл
2020
2121## Общие свойства
2222
2323Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :
2424
2525- Left, Top, Width, Height
2626- AutoSize: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления
2727- [[Autosize / Layout/ru|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне \#Label.
2828- Align
2929- Constraints: позволяет установить минимум и максимум для ширины и высоты
3030- [[Autosize / Layout/ru|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления
3131- [[Autosize / Layout/ru|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления
3232
3333Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].
3434
3535# Фиксированный дизайн
3636
3737Фиксированный ​​дизайн установлен по умолчанию. Якорь установлен в значение `[akLeft, akTop]`, что означает, значения Top и Left не будут изменяться LCL. Если значение `AutoSize` равно False, LCL не изменяет ширину или высоту. Если значение `AutoSize` равно True, то ширина и высота изменяются по размеру содержимого. Например, `TLabel.AutoSize` умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и `TLabel`, чтобы измененный текст поместился в контроле полностью. `TButton.AutoSize` умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения `Button.AutoSize` в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении `Caption` на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается. В любой момент вы можете изменить свойства `Left`, `Top`, `Width` и `Height`.
3838
3939# Автомасштаб
4040
4141`AutoSize`(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.
4242
4343Обычно `AutoSize` равенTrue и делает две вещи для видимого элемента управления:
4444
4545- Если возможно, изменяет управление в нужный размер. Например ширина и высота `TButton` изменяется в соответствии с надписью, в то время как `TEdit` изменяется только в высоту. Ширина `TEdit` не изменяется автоматически. Вы можете изменить ширину `TEdit` самостоятельно.(см GetPreferredSize).
4646- Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства `BorderSpacing`) и самый верхний дочерний элемент управления имел Top = 0.
4747
4848## Отличия от Delphi
4949
5050- `AutoSize` в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из `TLabel` изменяется. В LCL `AutoSize` всегда активен. Delphi позволяет изменить размер `TLabel`, который имеет AutoSize = True, LCL - нет.
5151- Скрытые управления не изменяют размер автоматически.
5252- Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании `akRight`, `akBottom` якоря с установленными `AnchorSides` и `BorderSpacing` сохраняют правильное расстояние.
5353
5454## Автомасштаб и изменение размера элемента управления
5555
5656С помощью свойства кнопки `AutoSize=false` задаются фиксированный размер по умолчанию.
5757
5858![](/wiki-images/1/1f/Autosize1.png)
5959
6060При установке `AutoSize=true` для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.
6161
6262![](/wiki-images/7/75/Autosize_on.png)
6363
6464`AutoSize` не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод `GetPreferredSize` элемента управления, который вызывает метод `CalculatePreferredSize`. По умолчанию реализация `TWinControl` запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод `CalculatePreferredSize`. Например, `TImage` переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг `ControlStyle = csAutoSize`, который в настоящее время используется только `TPanel`).
6565
6666`TWinControl` вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.
6767
6868Когда элемент управления привязан как слева, так и справа, его *ширина фиксирована*. Например, со значением привязки `Align=alTop` элемент управления привязан слева и справа, и соответствует ширине Родителя. Если `Parent.AutoSize` имеет значение true, тогда *Родитель* будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize / Layout/ru|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы (**BaseBounds** или **ReadBounds**). Если никаких ограничений не было установлено, используется метод `GetControlClassDefaultSize`. То же самое для свойства `Height` и привязки сверху и снизу.
6969
7070Ограничения применяются всегда и имеют приоритет.
7171
7272## Автомасштаб и перемещение дочерних элементов управления
7373
7474Когда *AutoSize=false*, вы можете размещать и перемещать элементы управления свободно:
7575
7676![](/wiki-images/7/75/Autosize_on.png)
7777
7878Когда *AutoSize=true*, дочерние элементы управления с фиксированным положением перемещаются с подгонкой.
7979
8080------------------------------------------------------------------------
8181
8282Прим.переводчика: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.
8383
8484------------------------------------------------------------------------
8585
8686![](/wiki-images/d/d9/Autosize_panel_on.png)
8787
8888Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства *BorderSpacing\>0* (у кнопок) или *Panel.ChildSizing.LeftRightSpacing\>0* (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.
8989
9090Могут быть перемещены дочерние элементы управления только со следующими свойствами:
9191
9292- Anchors=\[akLeft,akRight\]
9393- AnchorSide\[akLeft\].Control=nil
9494- AnchorSide\[akTop\].Control=nil
9595- Align=alNone
9696
9797Перемещение дочерних элементов управления зависит от свойства [[Autosize / Layout|ChildSizing.Layout]]. Разметка применяется в методе *TWinControl.AlignControls*, который может быть полностью или частично переопределен. Например, *TToolBar* переопределяет *ControlsAligned*, чтобы разместить все элементы управления со значением свойства *Align=alCustom*, и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.
9898
9999Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства *ControlStyle* флагов *csAutoSizeKeepChildLeft* и *csAutoSizeKeepChildTop* (начиная с 0.9.29).
100100
101101## Автомасштаб и формы
102102
103103Формы без Parent родительского элемента управления управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера в RunTime является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 px, а widgetset в ответ изменит размер к 800 px. Если вы устанавливаете свойство *Witdh* в событии формы *OnResize*, то можете создать бесконечное зацикливание. Вот почему LCL TForm отключает *AutoSize*, когда widgetset изменяет размеры формы.
104104
105105Это означает, что действие свойства *AutoSize* для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся текущие границы окна. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как "прилипающие" края, которые изменяют размеры текущего окна в привязке к другим окнам.
106106
107107### Принудительная установка автомасштабирования формы
108108
109109Вы можете выставить новое значение свойства *AutoSize*, выполнив:
110110
111111``` pascal
112112Form1.AutoSize := False;
113113Form1.AutoSize := True;
114114```
115115
116116### Расчет размера формы с "автомасштабом" заранее
117117
118118При размещении формы с "автомасштабом" (очевидно, имеется ввиду *Form1.AutoSize=True*) вам может потребоваться получить размер формы, прежде чем показывать ее. Для установки автомасштаба требуется дескриптор (хэндл окна). Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:
119119
120120``` pascal
121121Form1.HandleNeeded;
122122Form1.GetPreferredSize(PreferredWidth,PreferredHeight);
123123```
124124
125125> **Note:** Диспетчер окон и события формы могут изменять размер окна. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.
126126
127127# Привязка сторон (Anchoring)
128128
129129Элементы управления для привязки к четырем сторонам имеют свойство *Anchors* с возможными значениями: *akLeft*, *akTop*, *akRight* и *akBottom*. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает **сохранение расстояния** между привязанными элементами управления. По умолчанию значение свойства *Anchors = \[akLeft, akTop\]*. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как *Align* и *Parent.AutoSize* имеют более высокий приоритет, и могут изменять поведение.
130130
131131## Привязка к родителю или nil
132132
133133Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.
134134
135135Существует четыре комбинации akLeft, akRight (akTop, akBottom):
136136
137137- **akLeft, no akRight**: **Left** контрола фиксирован и не может быть изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что **Width** также сохраняется.
138138- **akLeft and akRight**: **Left** элемента управления фиксирован и не может быть изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда **Width** контрола также будет увеличена на 100 пикселей.
139139- **akRight, no akLeft**: левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.
140140- **no akLeft and no akRight**: ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.
141141
142142### Изменение размера родителя с привязанным контролом
143143
144144При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено свойство AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.
145145
146146GroupBox с кнопкой: ![](/wiki-images/6/60/Anchors1.png)
147147
148148Когда GroupBox увеличивается в размере, расстояние до привязанной стороны сохраняется:
149149
150150С akLeft, без akRight: ![](/wiki-images/0/05/Anchors_akLeft.png)
151151
152152С akLeft и akRight: ![](/wiki-images/6/66/Anchors_akLeft_akRight.png)
153153
154154С akRight, без akLeft: ![](/wiki-images/d/d3/Anchors_akRight_no_akLeft.png)
155155
156156Три кнопки в GroupBox: ![](/wiki-images/4/48/Anchors_no_akLeft_no_akRight_small.png)
157157
158158Без akLeft и без akRight их центры масштабируются: ![](/wiki-images/7/7b/Anchors_no_akLeft_no_akRight_big.png)
159159
160160**На заметку:**
161161
162162- Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений , сохраненных в файле формы lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.
163163- Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.
164164
165165### Изменение размера привязанного элемента управления
166166
167167При изменении ширины привязанного элемента управления, например, через Object Inspector или через код *Button1.Width:=3*, вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:
168168
169169#### Привязка к nil
170170
171171![](/wiki-images/b/bd/Anchored_right_nil_resize1.png) Кнопка Button1 привязана \[akTop,akRight\], AnchorSide\[akRight\].Control=nil
172172
173173![](/wiki-images/a/a0/Anchored_right_nil_resize2.png) Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя значение *Left* кнопки, увеличивая расстояние от правой стороны кнопки до правого края.
174174
175175![](/wiki-images/1/16/Anchored_right_nil_resize3.png) При изменении размера Groupbox кнопка сохранит новое расстояние до правого края.
176176
177177**Объяснение:** установка значения **Width** кнопки эквивалентна вызову SetBounds(**Left**,Top,**NewWidth**,Height). Вот почему значение **Left** сохраняется. Это совместимо с поведением контролов в Delphi.
178178
179179#### Привязка к родителю
180180
181181![](/wiki-images/4/45/Anchored_right_parent_resize1.png) Кнопка Button1 привязана \[akTop,akRight\], AnchorSide\[akRight\].Control=Button1.Parent
182182
183183![](/wiki-images/0/04/Anchored_right_parent_resize2.png) Установка значения *Width* на меньшее значение приведет к сужению кнопки, сохранив расстояние от ее правой границы до края компонента-родителя и изменив значение *Left* кнопки.
184184
185185## Привязка к соседу
186186
187187Вы можете привязываться к соседним элементам управления. Следующий пример показывает, что:
188188
189189- вы можете привязать левый край label'а к левому краю кнопки
190190- привязать верхний край label'а к нижнему краю кнопки
191191- привязать центр label'а к центру кнопки\
192192
193193![](/wiki-images/e/ee/Anchorsides_example1.png)\
194194![](/wiki-images/b/ba/Anchorside_example2.png)\
195195
196196Подробнее о том, как установить привязку, см. [[Anchor Sides/ru|Стороны привязки]].\
197197
198198![](/wiki-images/6/6d/Anchoreditor.png)
199199
200200## Зазор
201201
202202Свойство BorderSpacing(зазор) управляет минимальным количеством пространства вокруг элемента управления. Свойства:
203203
204204- **Around**: эта величина в пикселах, которая добавляется к каждому значению Left, Top, Right, Bottom элемента управления.
205205- **Left**: зазор в пикселах с левой стороны элемента управления
206206- **Top**: зазор в пикселах сверху элемента управления
207207- **Right**: зазор в пикселах с правой стороны элемента управления
208208- **Bottom**: зазор в пикселах под элементом управления
209209- **InnerBorder**: это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.
210210- **CellAlignHorizontal**: Этот параметр используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.
211211- **CellAlignVertical**: так же, как CellAlignHorizontal, но для вертикального выравнивания.
212212
213213### Правила зазоров
214214
215215- Значение Around добавляется к зазору для Left,Top,Right,Bottom элемента управления.
216216- Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют им растягиваться.
217217
218218#### Привязка к противоположной стороне
219219
220220Например, правая сторона A к левой стороне B.
221221
222222Используются обе границы A и B.
223223
224224- Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на общем родителе) он максимален, если:
225225 - LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around
226226 - RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around
227227 - Parent.ChildSizing.HorizontalSpacing
228228- Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на общем родителе) он максимален, если:
229229 - TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around
230230 - BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around
231231 - Parent.ChildSizing.VerticalSpacing
232232
233233Например:
234234
235235- если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px
236236- если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px
237237- если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px
238238
239239#### Привязка к этой же стороне
240240
241241Например, правая сторона A к правой стороне B.
242242
243243- Задействуется только зазор у границы А.
244244- Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.
245245
246246#### Привязка к центру
247247
248248Например, центр А привязан к центру В.
249249
250250Не задействуются ни зазор, ни свойство Parent.ChildSizing.Horizontal/VerticalSpacing.
251251
252252#### Пример
253253
254254Общим примером является привязка Label'а к Edit'у.\
255255![](/wiki-images/1/1e/BorderSpacing_Anchors.png)\
256256Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к наличию зазора в 6px между Label'ом и Edit'ом и наличию зазора в 6px слева от Label'а, а также наличию зазора в 6px справа от Edit'а. Также еще существует 6 пикселей зазора выше и ниже Edit'а.
257257
258258- Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, когда
259259 - Label1.BorderSpacing.Left + Label1.BorderSpacing.Around
260260 - GroupBox1.ChildSizing.LeftTopSpacing
261261
262262- Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, когда
263263 - Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around
264264 - GroupBox1.ChildSizing.RightBottomSpacing
265265
266266- Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все остальные зазоры игнорируются.
267267
268268- Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является
269269 - Control1.BorderSpacing.Left + Control1.BorderSpacing.Around
270270
271271Вот более сложный пример:
272272
273273![](/wiki-images/9/9c/Borderspacing_anchors2.png)
274274
275275Важные части кода lfm:
276276
277-``` pascal
277+``` lfm
278278 object GroupBox1: TGroupBox
279279 AutoSize = True
280280 Caption = 'GroupBox1'
281281 TabOrder = 0
282282 object Label1: TLabel
283283 AnchorSideLeft.Control = GroupBox1
284284 AnchorSideTop.Control = Edit1
285285 AnchorSideTop.Side = asrCenter
286286 BorderSpacing.Around = 6
287287 Caption = 'Label1'
288288 end
289289 object Edit1: TEdit
290290 AnchorSideLeft.Control = Label1
291291 AnchorSideLeft.Side = asrBottom
292292 AnchorSideTop.Control = GroupBox1
293293 AnchorSideRight.Control = GroupBox1
294294 AnchorSideRight.Side = asrBottom
295295 Anchors = [akTop, akLeft, akRight]
296296 BorderSpacing.Around = 6
297297 TabOrder = 0
298298 Text = 'Edit1'
299299 end
300300 object Label2: TLabel
301301 AnchorSideLeft.Control = GroupBox1
302302 AnchorSideTop.Control = Edit1
303303 AnchorSideTop.Side = asrBottom
304304 BorderSpacing.Around = 6
305305 Caption = 'Label2'
306306 end
307307 object ComboBox1: TComboBox
308308 AnchorSideLeft.Control = Label2
309309 AnchorSideTop.Control = Label2
310310 AnchorSideTop.Side = asrBottom
311311 AnchorSideRight.Control = GroupBox1
312312 AnchorSideRight.Side = asrBottom
313313 Anchors = [akTop, akLeft, akRight]
314314 BorderSpacing.Right = 6
315315 TabOrder = 1
316316 Text = 'ComboBox1'
317317 end
318318 object CheckBox1: TCheckBox
319319 AnchorSideLeft.Control = GroupBox1
320320 AnchorSideTop.Control = ComboBox1
321321 AnchorSideTop.Side = asrBottom
322322 BorderSpacing.Around = 6
323323 Caption = 'CheckBox1'
324324 TabOrder = 2
325325 end
326326 object Label3: TLabel
327327 AnchorSideLeft.Control = GroupBox1
328328 AnchorSideTop.Control = Edit2
329329 AnchorSideTop.Side = asrCenter
330330 BorderSpacing.Around = 6
331331 Caption = 'Label3'
332332 end
333333 object Edit2: TEdit
334334 AnchorSideLeft.Control = Label3
335335 AnchorSideLeft.Side = asrBottom
336336 AnchorSideTop.Control = CheckBox1
337337 AnchorSideTop.Side = asrBottom
338338 BorderSpacing.Around = 6
339339 TabOrder = 3
340340 Text = 'Edit2'
341341 end
342342 end
343343```
344344
345345### Зазор и выравнивание
346346
347347Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.
348348
349349- Обычно два Memo заполняют весь GroupBox.
350350- Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет присутствовать зазор в 10 пикселей.
351351- Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который от Memo1 до Memo2 составит 20px.
352352- Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется зазор в 50 пикселей.
353353- GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через значения свойств ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все упомянутые значения равны 0).\
354354
355355![](/wiki-images/1/13/Borderspacing_align1.png)\
356356
357357# Выравнивание (Aligned)
358358
359359Свойство Align(выравнивание) работает почти так же, как в Delphi, и может использоваться для быстрого заполнения какой-нибудь области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient. Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия друг друга. Это означает, что все элементы управления свойства Align со значением *alLeft, alTop, alBottom, alRight* не будут перекрываться, если есть достаточно места.
360360
361361Алгоритм работает следующим образом:
362362
363363- Сначала все элементы управления с помощью значения свойства alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина всех элементов остается максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и значение свойства Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.
364364- Затем все элементы управления с помощью значения свойства alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.
365365- Потом все элементы управления с помощью значения свойства alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.
366366- Затем все элементы управления с помощью значения свойства alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.
367367- Если есть элемент управления с установленным свойством alClient, он заполнит оставшуюся часть клиентской области.
368368- Если имеется более одного элемента управления с установленным свойством alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них будет показан.\
369369 \
370370
371371![](/wiki-images/e/e6/Autosize_align.png)
372372
373373## Выравнивание и зазоры
374374
375375Величина свойства BorderSpacing и свойства ChildSize родителя применяется к выравниваемым элементам управления. Элемент управления memo ниже имеет Align=alClient.\
376376
377377------------------------------------------------------------------------
378378
379379Прим.переводчика: Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.
380380
381381------------------------------------------------------------------------
382382
383383![](/wiki-images/b/b4/Autosize_align_borderspacing.png)
384384
385385## Выравнивание и Привязка
386386
387387Свободная сторона выровненного элемента управления (напр., правая сторона **Align=alLeft**) следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять значение своей **Width**. Если привязка установлена, то значение **Width** изменится.
388388
389389## Выравнивание и свойство AutoSize
390390
391391Элемент управления, выровненный посредством alLeft или alRight, растягивается по вертикали и будет использовать свою ширину, определенную на этапе проектирования формы. Если свойство *AutoSize* установлено в *true*, то значение параметра *Width* будет оптимальной шириной. Кнопка снизу имеет *Align=alRight* и *AutoSize=true*.\
392392![](/wiki-images/3/35/Autosize_align_autosize.png)
393393
394394## Выравнивание и свойство AutoSize родителя
395395
396396Элемент управления, выровненный посредством alClient, заполняет оставшееся пространство. Родительский элемент управления со значением свойства *AutoSize=true* при этом будет растягиваться или сжиматься так, чтобы впритирку умещать на себе свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?\
397397![](/wiki-images/b/b1/Autosize_nested1.png)\
398398LCL использует *предпочтительный размер* кнопок и потому растягивает или сжимает groupbox:
399399
400400![](/wiki-images/2/28/Autosize_nested2.png)\
401401
402402## alCustom
403403
404404Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления с установленным значением alCustom не перемещаются LCL, но могут быть перемещены вашим пользовательским элементом управления. Для этого вы должны переопределить методы `CalculatePreferredSize` и `DoAutoSize`.
405405
406406## Порядок расположения элементов управления с одинаковым значением Align
407407
408408Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим значением Left, для alTop - наименьшим значением Top, для alRight - наибольшая величина Left+Width, для alBottom - наибольшая величина Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.
409409
410410> **Note:** до версии Lazarus 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию элементов управления и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с выравниванием alTop или alLeft без указания границ, все элементы управления будут помещены в координаты 0.0, и, следовательно, последние добавленные окажутся поверх контролов, добавленных ранее.
411411
412412# Разметка (Layout)
413413
414414## Строки, столбцы и строки
415415
416416Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:
417417
418418- ChildSizing.Layout=cclLeftToRightThenTopToBottom
419419- ChildSizing.ControlsPerLine=3
420420- AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)\
421421
422422![](/wiki-images/1/10/Autosize_layout_lefttoright.png)
423423
424424Свойство **Layout** по умолчанию \[имеет значение\] cclNone. Если вы установите **Layout** в другое значение, каждый дочерний элемент, имеющий *нормальные* привязки, будет выровнен. *Нормальные* привязки означают:
425425
426426- Anchors=\[akLeft,akTop\]
427427- AnchorSide\[akLeft\].Control=nil
428428- AnchorSide\[akTop\].Control=nil
429429- Align=alNone
430430
431431Значение **cclLeftToRightThenTopToBottom** будет помещать первый дочерний элемент управление вверху \[и\] слева, второй - \[вверху и\] правее, и так далее. Это - **линия**. Свойство **ControlsPerLine** определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует \[всего\] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет \[по\] 3 элемента управления (столбцы). Если *ControlsPerLine* равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.
432432
433433Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не \[приходит\] из LCL.
434434
435435## Фиксированные зазоры между строками и столбцами
436436
437437Вы можете добавить зазоры между \[строками и столбцами\] этими свойствами:
438438
439439- ChildSizing.VerticalSpacing - зазор между строками
440440- ChildSizing.HorizontalSpacing - зазор между столбцами
441441- ChildSizing.LeftRightSpacing - зазор между левой и правой \[стороной\] *всех* столбцов
442442- ChildSizing.TopBottomSpacing - зазор выше и ниже *всех* столбцов
443443
444444Выше приведенный пример с \[параметрами\]:\
445445ChildSizing.VerticalSpacing=6,\
446446ChildSizing.HorizontalSpacing=15,\
447447ChildSizing.LeftRightSpacing=30,\
448448ChildSizing.TopBottomSpacing=10,\
449449AutoSize=true\
450450![](/wiki-images/a/a9/Autosize_layout_parentspacing.png)
451451
452452Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с \[помощью\] его свойства BorderSpacing.
453453
454454## Растяжение
455455
456456Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию *ChildSizing.EnlargeHorizontal=crsAnchorAligning* запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.
457457
458458- `crsAnchorAligning` - не использует дополнительные зазоры
459459- `crsScaleChilds` - умножает ширину/высоту на тот же коэффициент
460460- `crsHomogeneousChildResize` - добавляет к каждой ширине/высоте ту же величину
461461- `crsHomogeneousSpaceResize` - добавляет к каждому зазору между дочерними элементами ту же величину
462462
463463### crsScaleChilds
464464
465465`ChildSizing.EnlargeHorizontal=crsScaleChilds`\
466466`ChildSizing.EnlargeVertical=crsScaleChilds`\
467467`AutoSize=false`\
468468![](/wiki-images/b/b0/Autosize_layout_scalechilds.png)
469469
470470Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.
471471
472472### crsHomogeneousChildResize
473473
474474`ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize`\
475475`ChildSizing.EnlargeVertical=crsHomogeneousChildResize`\
476476`AutoSize=false`\
477477![](/wiki-images/3/39/Autosize_layout_homogeneouschildresize.png)
478478
479479Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.
480480
481481### crsHomogeneousSpaceResize
482482
483483`ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize`\
484484`ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize`\
485485`AutoSize=false`\
486486![](/wiki-images/9/96/Autosize_layout_homogeneousspaceresize.png)
487487
488488Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится \[дополнительно по\] 10 пикселей зазора слева, справа и между каждым дочерним элементом.
489489
490490## Сжатие
491491
492492Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. **ShrinkHorizontal**, **ShrinkVertical**.
493493
494494## Отдельные ячейки
495495
496496В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю *ячейку*. *Ячейка* - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств **BorderSpacing.CellAlignHorizontal** и **BorderSpacing.CellAlignVertical**.
497497
498498Например, установите *BorderSpacing.CellAlignHorizontal* пятой кнопки в **caCenter** \[и\] вы получите следующее:
499499
500500![](/wiki-images/d/d3/Autosize_layout_cellhorizontal_cacenter.png)
501501
502502Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:
503503
504504- `caFill`: дочерний элемент управления заполняет всю ширину (высоту) ячейки
505505- `caCenter`: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке
506506- `caLeftTop`: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева
507507- `caRightBottom`: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю
508508
509509![](/wiki-images/c/c0/Autosize_layout_cellalign.png)\
510510
511511# Пользовательская разметка через OnResize/OnChangeBounds
512512
513513Иногда разметка LCL недостаточно \[оптимальна\]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.\
514514![](/wiki-images/4/4b/Autosize_onresize.png)
515515
516516Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:
517517
518518``` pascal
519519procedure TForm1.GroupBox1Resize(Sender: TObject);
520520begin
521521 ListBox1.Width := GroupBox1.ClientWidth div 3;
522522end;
523523```
524524
525525### Общая ошибка: неправильное событие OnResize
526526
527527Не помещайте *весь* свой код изменения размера \[элементов управления\] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще \[будет\] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.
528528
529529### Общая ошибка: Width вместо ClientWidth, AdjustClientRect
530530
531531\[Свойство\] Width - это размер \[элемента управления\], включая рамку. \[Свойство\] ClientWidth - это внутренняя ширина \[элемента управления\] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:
532532
533533``` pascal
534534procedure TForm1.Panel1Resize(Sender: TObject);
535535var
536536 r: TRect;
537537begin
538538 r := Panel1.ClientRect;
539539 Panel1.AdjustClientRect(r);
540540 ListBox1.Width := (r.Right - r.Left) div 3;
541541end;
542542```
543543
544544# Пользовательские элементы управления
545545
546546Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL.
547547
548548## SetBounds, ChangeBounds, DoSetBounds
549549
550550**SetBounds** вызывается, когда устанавливаются свойства Left, Top, Width, Height, BoundsRect, или пользователь вызывает его напрямую. SetBounds обновляет BaseBounds и BaseParentClientSize, которые используются \[механизмом\] привязки для сохранения размеров. Например, загрузка формы с TMemo и lfm\[-файлом\], содержащим \[значения параметров\] Left и Width \[компонента\] TMemo, вызывает \[метод\] SetBounds для TMemo два раза. Когда пользователь распахивает окно, SetBounds вызывается для формы, но не для Memo, сохраняя BaseBounds Memo. Если Memo привязан справа, \[значение параметра\] Width \[компонента\] Memo изменяется на основе \[кода методов\] BaseBounds и BaseParentClientSize. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Delphi чаще всего называет SetBounds. SetBounds вызывает ChangeBounds с KeepBase=false.
551551
552552**ChangeBounds** вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью \[механизма разметки\] LCL. SetBounds вызывает внутри \[себя\] ChangeBounds с KeepBase=false, в то время как \[механизма разметки\] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.
553553
554554**DoSetBounds** - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, \[потому что\] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.
555555
556556**DoAdjustClientRectChange** вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.
557557
558558**WMSize** существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.
559559
560560## AdjustClientRect
561561
562562Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на \[свойства\] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный \[механизм\] привязки (например, установку).
563563
564564Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод **AdjustClientRect**:
565565
566566``` pascal
567567 TCustomPanel = class(TCustomControl)
568568 ...
569569 protected
570570 ...
571571 procedure AdjustClientRect(var aRect: TRect); override;
572572 ...
573573
574574procedure TCustomPanel.AdjustClientRect(var aRect: TRect);
575575var
576576 BevelSize: Integer;
577577begin
578578 inherited AdjustClientRect(aRect);
579579
580580 BevelSize := BorderWidth;
581581 if (BevelOuter <> bvNone) then
582582 inc(BevelSize, BevelWidth);
583583 if (BevelInner <> bvNone) then
584584 inc(BevelSize, BevelWidth);
585585
586586 InflateRect(aRect, -BevelSize, -BevelSize);
587587end;
588588```
589589
590590### AdjustClientRect и выравнивание
591591
592592AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями \[автоматической установки\] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством \[изменения величины\] зазора:
593593
594594![](/wiki-images/c/ca/Adjustclientrect_align.png)
595595
596596\[Кнопка\] Button1 на скриншоте была создана со \[значением\] Align=alClient. Также \[был\] задействован \[свойство панели\] ChildSizing.Layout.
597597
598598Когда \[дочерний элемент\] привязывается к родительскому элементу через \[свойство\] AnchorSides, также используется параметр AdjustClientRect:
599599
600600``` pascal
601601 Button1.AnchorParallel(akTop,0,Button1.Parent);
602602```
603603
604604Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к \[значению\] Top, Button1.Top будет 3px (3 плюс 0).
605605
606606## Собственный AutoSize
607607
608608Когда параметру *AutoSize* установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.
609609
610610### Предпочтительный размер
611611
612612Новый размер выбирается \[механизмом\] LCL через \[метод\] **GetPreferredSize**, который вызывает **CalculatePreferredSize** \[и\] который можно переопределить. Например, давайте опишем **TQuadrat**, который является \[наследником\] TShape, но его высота должна быть равна его ширине:
613613
614614``` pascal
615615 TQuadrat = class(TShape)
616616 protected
617617 procedure CalculatePreferredSize(var PreferredWidth,
618618 PreferredHeight: integer; WithThemeSpace: Boolean); override;
619619 end;
620620...
621621procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,
622622 PreferredHeight: integer; WithThemeSpace: Boolean);
623623begin
624624 PreferredHeight:=Width;
625625end;
626626```
627627
628628Метод CalculatePreferredSize получает два переменных параметра: *PreferredWidth* и *PreferredHeight*. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает \[свойство\] *PreferredHeight* в текущее \[свойство\] *Width*. Логический параметр *WithThemeSpace* устарел и всегда \[равен\] false.
629629
630630**Важно**: \[метод\] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать \[механизм\] autosize. Это создаст \[бесконечный\] цикл.
631631
632632Вычисление *PreferredWidth/Height* может быть дорогостоящей \[операцией\]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана \[процедура\] *InvalidatePreferredSize*. В нашем примере *PreferredHeight* зависит от \[параметра\] *Width*, поэтому мы должны \[вызвать\] перерисовку \[элемента управления\], когда изменяется \[параметр\] *Width*:
633633
634634``` pascal
635635 TQuadrat = class(TShape)
636636 protected
637637 ...
638638 procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
639639 end;
640640...
641641procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
642642begin
643643 inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
644644 InvalidatePreferredSize;
645645 // Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.
646646end;
647647```
648648
649649LCL автоматически активирует \[механизм\] autosizing'а, когда границы изменяются, поэтому пример является полным.
650650
651651По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение \[будет\] равно 0, и LCL будет сохранять текущую \[величину\] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle \[значение\] csAutoSize0x0 (ControlStyle:=ControlStyle+\[csAutoSize0x0\];). Примером может служить\элемент управления LCL TPanel.
652652
653653### AdjustSize
654654
655655Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван \[механизм\] автоматического изменения размера. Например:
656656
657657``` pascal
658658procedure TQuadrat.SetSubTitle(const AValue: string);
659659begin
660660 if FSubTitle = AValue then exit;
661661 FSubTitle := AValue;
662662 InvalidatePreferredSize;
663663 AdjustSize;
664664end;
665665```
666666
667667# Сокращение накладных расходов с помощью \[методов\] DisableAutoSizing, EnableAutoSizing
668668
669669Начиная с \[версии\] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До \[версии\] 0.9.28 \[методы\] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.
670670
671671Каждый раз, когда вы меняете \[какое-нибудь\] свойство, LCL запускает \[механизм\] пересчета разметки:
672672
673673``` pascal
674674Label1.Caption := 'A'; // первый перерасчет
675675Label2.Caption := 'B'; // второй перерасчет
676676```
677677
678678Перерасчет будет возбуждать \[механизм вызова\] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить \[механизм\] автоизменения размера:
679679
680680``` pascal
681681DisableAutoSizing;
682682try
683683 Label1.Caption := 'A'; // нет перерасчета
684684 Label2.Caption := 'B'; // нет перерасчета
685685finally
686686 EnableAutoSizing; // однократный перерасчет
687687end;
688688```
689689
690690Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) \[вызова\] DisableAutoSizing.
691691
692692Начиная с \[версии Lazarus\] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для **всех** элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:
693693
694694``` pascal
695695procedure TMyRadioGroup.DoSomething;
696696begin
697697 DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы
698698 try
699699 // удаляем элементы ...
700700 // добавляем, тасуем элементы ...
701701 // меняем заголовки элементов ...
702702 finally
703703 EnableAutoSizing; // перерасчет
704704 end;
705705end;
706706```
707707
708708К чему все это: вам не нужно \[ни о чем\] заботиться. Просто вызовите Disable/EnableAutoSizing.
709709
710710> **Note:** вот так неправильно:\
711711> \
712712>
713713> ``` pascal
714714> Button1.DisableAutoSizing;
715715> Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный
716716> //счетчик ссылок на автоизменение размера
717717> ```
718718
719719## DisableAutoSizing и границы формы
720720
721721DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы \[можете\] встретить в системах Linux. Каждый раз, когда форма изменяет размер или перемещается, \[ее\] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными \[для отображения\].
722722
723723------------------------------------------------------------------------
724724
725725Прим.перев.: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши.
726726
727727------------------------------------------------------------------------
728728
729729# Пример: панель кнопок
730730
731731В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка *Help* слева и кнопки *Ok* и *Cancel* справа. Мы хотим, чтобы панель была внизу формы, заполняя всю \[ее\] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.
732732
733733Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.
734734
735735![](/wiki-images/7/7e/Autosize_example_buttonpanel1.png)
736736
737737Установите в свойстве *Kind* BitBtns \[соответственные\] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways\[("отображать всегда")\], чтобы увидеть их на своей платформе. Установите для свойства *AutoSize* \[кнопок\] BitBtn значение *True*, которое уменьшит/увеличит \[ширину\] кнопок для идеального заполнения \[их\] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.
738738
739739![](/wiki-images/4/49/Autosize_example_buttonpanel2.png)
740740
741741Установите свойство *Align ' кнопки справки в*alLeft*и установите для остальных двух кнопок свойство*Align*в*alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.
742742
743743![](/wiki-images/b/b6/Autosize_example_buttonpanel3.png)
744744
745745Высота панели по-прежнему фиксирована. Теперь установите для панели свойство *AutoSize* в *True*. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.
746746
747747![](/wiki-images/3/36/Autosize_example_buttonpanel4.png)
748748
749749Кнопка *Ok* имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки *Constraints.MinWidth* в значение *75*. Теперь кнопка несколько расширится.
750750
751751![](/wiki-images/3/32/Autosize_example_buttonpanel5.png)
752752
753753Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели *ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing* в значение *6*.
754754
755755![](/wiki-images/4/46/Autosize_example_buttonpanel6.png)
756756
757757Наконец, очистите *Caption* панели и установите ее 'BevelOuter*в*bvNone''.
758758
759759![](/wiki-images/4/4e/Autosize_example_buttonpanel7.png)\
760760
761761# Прокрутка
762762
763763Некоторые элементы управления LCL, такие как *TScrollBox*, *TForm' и*TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка **TScrollingWinControl**.
764764
765765Прокручиваемая **логическая клиентская область** элемента управления может быть больше, чем **видимая клиентская область**. **Видимая клиентская область** - это **ClientRect**. Она всегда начинается с \[координат\] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:
766766
767767``` pascal
768768ClientWidth <= Width
769769ClientHeight <= Height
770770```
771771
772772**Логическая клиентская область** определяется методом **GetLogicalClientRect**. По умолчанию она совпадает с *ClientRect*. Когда дочерний элемент управления привязан к правой стороне, он использует *логическую клиентская область*. *TScrollingWinControl* переопределяет этот метод и возвращает *Range*\[(диапазон)\] полос прокрутки, если \[логическая клиентская область\] больше, чем *ClientRect*. **Range** можно установить вручную или автоматически с помощью *AutoScroll=true*. Пример для *AutoScroll=true*:
773773
774774![](/wiki-images/d/db/Autoscroll_all_fit1.png)
775775
776776Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.
777777
778778Теперь панель сжата, так что ClientWidth становится меньше 200:
779779
780780![](/wiki-images/2/27/Autoscroll_not_fit1.png)
781781
782782Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.
783783
784784## Позиция прокрутки
785785
786786Изменение положения полосы прокрутки не изменяет \[значения\] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, \[а также\] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.
787787
788788## Прокрутка и автомасштаб
789789
790790Когда AutoSize=true, LCL расширяет \[родительский\] элемент управления \[так\], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если \[родительский\] элемент управления не может быть расширен, то (только) \[тогда проявляется\] вторичное действие AutoSize: перемещение дочерних элементов управления.
791791
792792# Пристыковка
793793
794794Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].
795795
796796# Splitter
797797
798798См. [[TSplitter/ru|TSplitter]].
799799
800800# TLabel.WordWrap
801801
802802**TLabel.WordWrap**\[(перенос слов)\] изменяет поведение предпочтительного размера \[компонента\] label. **WordWrap=true** требует, чтобы \[величина\] Width \[компонента\] label была фиксированной, например, путем привязки левого и правого размера \[компонента\] label. Предпочтительная высота label затем вычисляется путем разбиения \[своства\] Caption на несколько строк.
803803
804804# Автоматическая настройка DPI и автоматическая настройка статической разметки
805805
806806Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого \[рабочего стола\] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях \[на разметку\] в виде гибкой сетки. Существует выбор нескольких режимов, \[которые\] позволяют пересчитывать значения пикселей как в абсолютных \[величинах\], так и в корректируемых для DPI, или как считающихся просто частью размера формы.
807807
808808Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.
809809
810810В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого \[рабочего стола\] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.
811811
812812`property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;`
813813
814814Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy
815815
816816``` pascal
817817 TLayoutAdjustmentPolicy = (
818818 lapDefault, // Зависимость от виджетсета
819819 lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах
820820 lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,
821821 // ось x растягивается, чтобы заполнить экран и
822822 // y масштабируется, чтобы соответствовать DPI
823823 lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI
824824 );
825825```
826826
827827Следующие новые методы в TControl позволяют \[задать\] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:
828828
829829``` pascal
830830TControl = class
831831public
832832...
833833 procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;
834834 const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;
835835 function ShouldAutoAdjustLeftAndTop: Boolean; virtual;
836836 function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;
837837```
838838
839839LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.\
840840
841841# Подробнее
842842
843843Многие элементы управления переопределяют *TControl.DoAutoSize* для выполнения фактического автомасштабирования.
844844
845845ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.
846846
847847Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts\[(контексты устройств)\], которые необходимы для вычисления таких вещей, как размер текста.
848848
849849Вот почему вы всегда должны называть *AdjustSize* вместо `DoAutoSize`.
850850
851851`TControl.AdjustSize` вызывает `DoAutoSize` умным способом.
852852
853853Во время загрузки и создания дескриптора вызовы задерживаются.
854854
855855Этот метод изначально делает то же самое, что и *TWinControl.DoAutoSize*. Но поскольку `DoAutoSize` обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает `AdjustSize`.
856856
857857При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:
858858
859859LCL будет только автомасштабировать *Width* (Height), если \[это свойство\] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:
860860
861861- левая и правая стороны привязаны. Вы можете привязать стороны с \[помощью\] свойства **Anchors** или установить свойство *Align* в alTop, alBottom или alClient.
862862- \[параметр\] *Width* ограничен \[значением\] свойства *Constraints*. \[Свойство\] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.
863863
864864То же \[справедливо и для свойства\] *Height*.
865865
866866Новый размер рассчитывается protected-методом **TControl.CalculatePreferredSize**. Этот метод запрашивает виджетсет для \[получения\] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. \[У него\] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - \[LCL\] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому \[параметр\] AutoSize не влияет на TMemo.
867867
868868Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.
869869
870870Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).
871871
872872## Parent.AutoSize
873873
874874Выше описанное \[дает\] простое объяснение. Реальный алгоритм \[функционально\] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.
875875
876876## Свойства / Методы
877877
878878- `Left`
879879- `Top`
880880
881881Если Parent\<\>nil, то \[значения\] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда \[область\] *без* рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi \[с ним\] несовместим. Left и Top могут иметь отрицательные \[значения\] или \[быть\] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000\[px\] или более.
882882
883883Когда клиентская область прокручивается, Left и Top остаются неизменными.
884884
885885Во время изменения размера/перемещения \[элемента управления\] Left и Top не всегда синхронизируются с координатами дескриптора объекта.
886886
887887Если Parent=nil, тогда \[значения\] Left, Top зависят от виджетсета и диспетчера окон. До \[версии\] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это \[поведение\] на Left, Top окна.
888888
889889Подсказка: Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left *и* Top, используйте взамен:
890890
891891``` pascal
892892with Button1 do
893893 SetBounds(NewLeft, NewTop, Width, Height);
894894```
895895
896896- `Width`
897897- `Height`
898898
899899Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения \[свойства\] Width и Height не всегда синхронизируются с размером дескриптора объекта.\
900900
901901- `BoundsRect`
902902
903903Аналогично `Bounds(Left, Top, Width, Height)`.
904904
905905Обычная ошибка новичка:
906906
907907``` pascal
908908BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка
909909```
910910
911911Это не \[оказывает никакого\] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и
912912
913913``` pascal
914914var
915915 r: TRect;
916916begin
917917 r := BoundsRect; // получаем границы
918918 r.Left := 3; // изменяем значение в стеке
919919end; // нет изменений
920920```
921921
922922- `ClientRect`
923923
924924Left и Top всегда равны 0,0. \[Свойства\] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это \[то, что\] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.\
925925
926926- `ClientOrigin`
927927
928928Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение \[о\] перемещении в интерфейс. Интерфейс обрабатывает \[сообщения о\] перемещениях сразу или по очереди.\
929929
930930- `LCLIntf.GetClientBounds`
931931
932932Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox \[параметры\] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.\
933933
934934- `LCLIntf.GetWindowRect`
935935
936936После вызова \[этой функции\] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а \[значения\] Right и Bottom будут экранной координатой нижне-правого пиксела.\
937937
938938- `FBaseBoundsLock: integer`
939939
940940Увеличивается/уменьшается \[методами\] LockBaseBounds/UnlockBaseBounds. Используется для сохранения \[поля\] FBaseBounds во время вызова SetBounds.\
941941
942942- `FBaseParentClientSize: TPoint`
943943
944944Размер Parent.ClientRect действителен для FBaseBounds. \[Поля\] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для \[параметра выравнивания\] akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.\
945945
946946- `FBoundsRectForNewParent: TRect`
947947
948948При смене родителя элемента управления Дескриптор \[объекта\] создается \[заново\], и многое может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.\
949949
950950- `fLastAlignedBounds: TRect`
951951
952952Для получения пояснения см. `TControl.SetAlignedBounds`. Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.\
953953
954954- `FLastChangebounds: TRect`
955955
956956Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.\
957957
958958- `FLastDoChangeBounds: TRect`
959959
960960Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.\
961961
962962- `FLastResizeClientHeight: integer`
963963- `FLastResizeClientWidth: integer`
964964- `FLastResizeHeight: integer`
965965- `FLastResizeWidth: integer`
966966
967967Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.\
968968
969969- `FLoadedClientSize: TPoint`
970970
971971Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается *SetClientWidth/SetClientHeight* в конце загрузки. Таким образом, LCL может восстанавливать размеры (напр., при *akRight*), используемые во время проектирование.\
972972
973973- `FReadBounds: TRect`
974974
975975Аналогично `FLoadedClientSiz`, но для `SetLeft`, `SetTop`, `SetWidth`, `SetHeight`.\
976976
977977- `procedure SetBoundsRectForNewParent(const AValue: TRect);`
978978
979979Используется для установки `FBoundsRectForNewParent`. См. выше.\
980980
981981- `procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;`
982982
983983Как SetBounds, но без изменения размеров по умолчанию.\
984984
985985- procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;\<//tt\>
986986
987987"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.\
988988
989989- `procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;`
990990
991991Фиксирует текущие границы базовых границ.\
992992
993993- `procedure SetClientHeight(Value: Integer);`
994994- `procedure SetClientSize(Value: TPoint);`
995995- `procedure SetClientWidth(Value: Integer);`
996996
997997Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.\
998998
999999- `procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;`
10001000
10011001Это внутренний SetBounds. Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.\
10021002
10031003- `procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;`
10041004
10051005Этот \[метод\] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.\
10061006
10071007- `procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;`
10081008
10091009Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.
10101010
10111011- - игнорирует вызовы, когда границы заблокированы
10121012 - блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.\
10131013
10141014- `Function GetClientOrigin: TPoint; virtual;`
10151015
10161016Координаты экрана Left, Top клиентской области.\
10171017
10181018- `Function GetClientRect: TRect; virtual;`
10191019
10201020Размер клиентской области. (всегда Left=0, Top=0)\
10211021
10221022- `Function GetScrolledClientRect: TRect; virtual;`
10231023
10241024Видимая клиентская область ClientRect.\
10251025
10261026- `function GetChildsRect(Scrolled: boolean): TRect; virtual;`
10271027
10281028Возвращает клиентский прямоугольник относительно Left, Top элемента управления. Если \[свойство\] Scrolled \[имеет значение\] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).\
10291029
10301030- `function GetClientScrollOffset: TPoint; virtual;`
10311031
10321032Возвращает смещение прокрутки клиентской области.\
10331033
10341034- `function GetControlOrigin: TPoint; virtual;`
10351035
10361036Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране). Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. \[А уж\] интерфейс обрабатывает перемещаемый дескриптер сразу или в \[порядке\] очереди.\
10371037
10381038# ЧаВо
10391039
10401040## Почему \[свойство\] AutoSize не работает в дизайнере должным образом?
10411041
10421042В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, \[свойство\] AutoSizing не обновляется при каждом изменении во время разработки.
10431043
10441044## Почему TForm.AutoSize не работает, когда что-то меняется?
10451045
10461046См. [[Autosize / Layout/ru|Автомасштаб и формы]]
10471047
10481048## Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?
10491049
10501050Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:
10511051
10521052``` pascal
10531053procedure TFrom.Button1Click(Sender: TObject);
10541054begin
10551055 // изменяем ширину элемента управления
10561056 Button1.Width := Button1.Width + 10;
10571057 // применяем все необходимые изменения и перерисовываем кнопку
10581058 Application.ProcessMessages;
10591059 // делаем много вещей, которые занимают много времени
10601060 ...
10611061 // после выхода из OnClick LCL автоматически обрабатывает сообщения
10621062end;
10631063```
10641064
10651065## При включении привязки во время исполнения \[приложения\] элементы управления изменяют размеры, но не используют текущие значения. Почему?
10661066
10671067akBottom означает: сохранить расстояние до нижней части родителя. Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом \[методов\] SetBounds или UpdateBaseBounds.
10681068
10691069Например: TListBox (Anchors=\[akLeft,aTop\]) при проектировании имеет нижний зазор в 100 пикселей. И кнопку для включения/выключения \[значения\] akBottom TListBox'а. Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы\[, заданные\] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено. Чтобы использовать текущие границы в качестве базовых, используйте:
10701070
10711071``` pascal
10721072ListBox1.UpdateBaseBounds(true, true, false);
10731073ListBox1.Anchors := ListBox1.Anchors + [akBottom];
10741074```
10751075
10761076Установка привязки не вызывает автоматически \[вызов метода\] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.
10771077
10781078## Изменение размера столбцов stringgrid в событии OnResize формы не работает
10791079
10801080\[Событие\] OnResize формы запускается, когда изменяются \[значения\] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит \[непосредственно\] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает\[, что в таких случаях\] использование \[события\] OnResize формы часто будет работать, но не всегда. Ярким примером \[того, что использование OnResize формы\] всегда терпит неудачу, является \[случай\], когда тема изменена, и у вас есть TStringGrid\[, лежащий\] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого \[события\] OnResize формы не запускается. Но меняется \[соответственно теме\] TGroupBox, поэтому TStringGrid должен быть изменен.
10811081
10821082Решение: используйте \[событие\] OnResize TStringGrid'а.
10831083
10841084## Почему TForm.Width равен TForm.ClientWidth?
10851085
10861086Примечание Mattias'а:
10871087
10881088"Есть исторические и технические причины.
10891089
10901090Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, \[размер рамки\] доступен только после \[наступления\] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal. Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.
10911091
10921092Существует новое определение компилятора **LCLRealFormBounds** в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью `LCLRealFormBounds ON`. Пока что поддерживается только widgetset win32.
10931093
10941094Для всех остальных элементов управления применяются правила ClientWidth\<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."
10951095
10961096## Я получаю бесконечный цикл / Как отладить автомасштабирование?
10971097
10981098Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:
10991099
11001100### Отличия от Delphi
11011101
11021102Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize / Layout/ru|Отличия от Delphi]]
11031103
11041104### Переопределение метода [[LCL/ru|LCL]] и запуск пересчета
11051105
11061106Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:
11071107
11081108``` pascal
11091109procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
11101110begin
11111111 // Это создаст бесконечный цикл
11121112 InvalidatePreferredSize;
11131113
11141114 // Это также создаст бесконечный цикл:
11151115 OtherComponent.Left:=10;
11161116 OtherComponent.Left:=20;
11171117
11181118 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
11191119end;
11201120```
11211121
11221122Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".
11231123
11241124Решение: отслеживайте изменения:
11251125
11261126``` pascal
11271127procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
11281128begin
11291129 if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then
11301130 InvalidatePreferredSize;
11311131 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
11321132end;
11331133```
11341134
11351135#### TWinControl.AlignControls
11361136
11371137``` pascal
11381138procedure AlignControls(aControl: TControl; var aRect: TRect); override;
11391139```
11401140
11411141AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.
11421142
11431143Параметр `aControl: TControl` поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align. Если у вас есть, например, два элемента управления A,B со \[значением\] Align=alLeft, то \[элемент управления, который будет\] нижним слева расположится левее \[другого\]. Если оба имеют одинаковые \[значения\] Left, \[то\] приоритетнее \[элемент в\] порядке создания. Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке \[значения\] B.Left в 0. Теперь AlignControls запускается и находит\[, что у обоих элементов управления значения\] A.Left=0 и B.Left=0. В обычном \[случае\] A \[по расположению окажется в\] приоритете. Чтобы \[приоритетнее оказался элемент управления\] B, VCL вызовет AlignControls(B,r). Таким образом, aControl является последним перемещенным. В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.
11441144
11451145Смотри `TWinControl.CreateControlAlignList`.\
11461146
11471147### OnResize/OnChangeBounds конфликтует со свойствами LCL
11481148
11491149Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize \[имеет значение\] true. Если вы определяете Label1.Width в событии OnResize, LCL будет \[запускать\] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в \[бесконечный\] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:
11501150
11511151``` pascal
11521152procedure TMainForm.FormResize(Sender: TObject);
11531153begin
11541154 // если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:
11551155 Button1.Width:=100;
11561156end;
11571157```
11581158
11591159### Ошибка интерфейса LCL, пользовательский виджет
11601160
11611161Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с \[опцией\] `-dVerboseIntfSizing`. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать \[об этом\] LCL через Constraints\[(ограничения)\]. Поищите `SetInterfaceConstraints` в различных интерфейсах LCL и `TWSControlClass.ConstraintWidth/Height`.
11621162
11631163### alCustom
11641164
11651165Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize / Layout/ru|alCustom]].
11661166
11671167# См. также
11681168
11691169- [[Docking]]
11701170- [[Anchor Sides/ru|Anchor Sides]]
11711171
11721172---
11731173*From the Free Pascal wiki, page "Autosize / Layout/ru", as it stood on 2023-09-05 after a change by Zoltanleo, 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