Способов ресайзинга (Resize) форм много. Даже очень
много. У каждого свой ;)
До я и сам решил создать форму с ресайзингом и.... создал
совершенно "ломовой" способ ;): просто в событии Form_Resize тупо передвигаем
каждый контрол, управляя его атрибутами .Top, .Left. Width
и .Heigth. Код получается простым, длинным и
малоэффективным. Следовательно, некрасивым....бррр.....
Если что-то некрасиво, то я спокойно смотреть на это не могу ;).
Начал искать по этой теме сэмплы, коды, исходники.... и совершенно случайно, можно сказать,
машинально, скачал с сайта CodeGuru пример
http://www.codeguru.com/code/legacy/vb_forms/FlexiForms_Src.zip . Пример
меня просто поразил своей простотой и элегантностью исполнения. Правда, он был
для VB6, пришлось повозиться с адаптацией под
Access. В
процессе возни я много чего упростил из примера (для повышения скорости ;) и уменьшения
кода. И вот представляю Вам вариант под Access:
Создаем два класса - первый
clsControl:
Option Compare
Database
Option Explicit
Public LeftIndent As
Long
Public RightIndent As Long
Public TopIndent As
Long
Public BottomIndent As
Long
Public Width As
Long
Public Height As
Long
Public style As
enLayoutStyle
Public ctrl As
Control
и второй класс clsLayOut:
Option
Compare
Database
Option Explicit
Public Enum
enLayoutStyle
lsNone = 0 ' Позиция и размер зафиксированы (нет
необходимости в использовании)
lsLeft = 1 ' контрол привязан к левой
границе формы (нет необходимости в использовании)
lsRight = 2 ' контрол привязан к правой стороне формы
lsHorizontal = 3 ' зафиксирован между
правыми и левыми
границами формы (растягивается горизонтально)
lsTop = 4 ' привязка к верхей границе
формы (нет необходимости в использовании)
lsBottom = 8 ' привязка к нижней границе
формы
lsVertical = 12 'зафиксирован между верхней и нижней
границами формы (растягивается вертикально)
lsFull = 15 ' контрол растягивается вертикально и
горизонтально по правой и нижней границе
lsXProp = 16 ' позиция зависит от ширины формы
(распределение по ширине)
lsYProp = 32 ' позиция зависит от высоты формы
(распределение по высоте)
lsWprop = 64 ' ширина пропорциональна ширине формы
lsDprop = 128 ' высота пропорциональна высоте формы
End Enum
' комбинация условий дает дополнительные преимущества -
так, например 160 (128+32) позволяет
' распределять контролы по высоте, одновременно их растягивая
' 80 (64+16) распределяет контролы по ширине формы, одновременно растягивая их,
например:
'
выровненные друг к другу поля или кнопки которые просто
расширяются, сохраняя строй ;)
' 10 (8+2) привязывает контрол
к правому нижнему углу формы
Private m_Controls As
Collection ' of clsControl objects
Private m_minWidth As
Long
Private m_minHeight As
Long
Private Function
AddControl(ctrl As Control, style
As Long)
On Error GoTo
errortrap
Dim udt As
New clsControl
udt.style = style
Set udt.ctrl = ctrl
udt.LeftIndent = ctrl.Left
udt.TopIndent = ctrl.Top
udt.RightIndent = ctrl.Parent.InsideWidth - (ctrl.Width + ctrl.Left)
udt.BottomIndent = ctrl.Parent.InsideHeight - (ctrl.Height + ctrl.Top)
udt.Width = ctrl.Width
udt.Height = ctrl.Height
m_Controls.Add udt
Exit Function
errortrap:
' there are some controls that have no visual element in
run time (ie timer, image list etc)
End Function
Private Function
NotNeg(x As Long)
NotNeg = IIf(x < 0, 0, x)
End Function
Public Function
RedrawLayout()
Dim Width As
Long
Dim Height As Long
Dim ctrl As clsControl
For Each ctrl
In m_Controls
With ctrl
If Width = 0 Then
Width = .ctrl.Parent.InsideWidth
If Width < m_minWidth Then Width =
m_minWidth
End If
If Height = 0 Then
Height = .ctrl.Parent.InsideHeight
If Height < m_minHeight Then Height =
m_minHeight
End If
If (.style And lsXProp) = lsXProp Then
.ctrl.Left = (.LeftIndent * Width) / m_minWidth
End If
If (.style And lsHorizontal) = lsHorizontal
Then
.ctrl.Width = NotNeg((Width - .RightIndent) - .LeftIndent)
Else
If (.style And lsWprop) = lsWprop Then
.ctrl.Width = (.Width * Width) / m_minWidth
End If
If (.style And lsRight) = lsRight Then
.ctrl.Left = NotNeg((Width - .RightIndent) - .ctrl.Width)
End If
End If
If (.style And lsYProp) = lsYProp Then
.ctrl.Top = (.TopIndent * Height) / m_minHeight
End If
If (.style And lsVertical) = lsVertical Then
.ctrl.Height = NotNeg((Height - .BottomIndent) - .TopIndent)
Else
If (.style And lsDprop) = lsDprop Then
.ctrl.Height = (.Height * Height) / m_minHeight
End If
If (.style And lsBottom) = lsBottom Then
.ctrl.Top = NotNeg((Height - .BottomIndent) - .ctrl.Height)
End If
End If
End
With
Next
End Function
Public Function
SetLayout(frm As Form)
m_minWidth = frm.InsideWidth
m_minHeight = frm.InsideHeight
Set m_Controls = New
Collection ' of clsControl objects
Dim con As Control
For Each con
In frm.Controls
' проверим Tag
на цифру
If
IsNumeric(con.Tag) Then
AddControl con,
con.Tag
End
If
Next
End Function
После
добавления классов в приложение достаточно в любой форме вставить такой простой код:
Option Compare
Database
Option Explicit
Private myLayout As
New clsLayOut
Private Sub Form_Load()
myLayout.SetLayout Me
End Sub
Private Sub
Form_Resize()
myLayout.RedrawLayout
End Sub
Для любого контрола (кнопка, поле, надпись, список, подчиненная форма и т.п.) в
дополнительных параметрах (Tag) укажите номер (цифру) стиля
(перечислен в
Public Enum enLayoutStyle)
и, в зависимости от указанного номера стиля, контрол будет изменять свое
положение и (или) размер.
Для подчиненных форм: также вставьте код для формы в подчиненной
форме и укажите для контролов подчиненной формы (и для
самого контейнера подчиненной формы!) номера стилей в дополнительных
параметрах (Tag) - всё также будет ресайзиться вместе с
главной формой.
Если у Вас в параметрах Tag уже что-то
хранится, не беда, алгоритм пропускает любые строковые выражения и обрабатывает
только цифры. Просто контрол не будет ресайзиться. Конечно, контрол не будет
ресайзиться, если Вы ничего не укажете в дополнительном параметре (Tag).
Ограничения: невозможно управлять размерами
контролов во фреймах и вкладках, хотя самими размерами фреймов и
вкладок управлять можно.
Практическая реализация:
код подчиненной формы я оставил простым, как показано выше, а код главной формы немного переработал, можно сказать,
добавил функциональности:
Option Compare
Database
Option Explicit
Private myLayout As
New clsLayOut
Private mInsideHeight As
Long
Private mInsideWidth As
Long
Private mIsMovedAfterOpen As
Boolean
Private Sub
Form_Current()
' можно запоминать размер формы при
закрытии, чтобы восстанавливать при открытии
If Not
mIsMovedAfterOpen Then
' "ломовое"
восстановление размеров
DoCmd.MoveSize 1500, 1500, 15000, 8000
mIsMovedAfterOpen = True
End If
End Sub
Private Sub Form_Load()
' запомним первоначальный "минимальный" размер формы
mInsideHeight = Me.InsideHeight
mInsideWidth = Me.InsideWidth
myLayout.SetLayout Me
Me.ОбластьДанных.Height = 30000 'чтобы не ограничивало
перемещение элементов по вертикали
'
Me.Detail.Height = 30000
End Sub
Private Sub
Form_Resize()
' ограничиваем размер формы по "минимальным"
размерам
If Me.InsideHeight < mInsideHeight
Then Me.InsideHeight = mInsideHeight
If Me.InsideWidth < mInsideWidth
Then Me.InsideWidth = mInsideWidth
myLayout.RedrawLayout
End Sub
На кнопку закрытия формы или события Form_Unload полезно повесить процедуру
сохранения размеров и положения формы, например, в реестре, чтобы при
следующем открытии сразу ресайзить
ее по сохраненному размеру.
(Например: в Access 2002-2003: Me.WindowLeft,
Me.WindowTop, Me.WindowWidth, Me.WindowHeight, хотя для универсальности
(и совместимости с Access
2000) я бы рекомендовал использовать
API)
Весь опубликованный
код работает в Access 2000
и в .MDE без проблем....
Осторожно!: при правке кода в
модуле формы с ресайзингом всегда сначала переключайте форму в режим
конструктора. Иначе при сохранении формы измененные размеры контролов (если Вы
растягивали их) также сохранятся! - будете потом чертыхаться и руками
восстанавливать первоначальные размеры ;)