Универсальный автоподбор высоты ленточной формы Access
1. Общая идея
Для любой ленточной формы высота окна считается по формуле:
Высота формы = (N + ExtraRows) × RowHeightCm + ExtraHeightCm
где:
N — текущее количество записей формы с учётом фильтра;
ExtraRows — временная добавка строк, например при добавлении новой записи;
RowHeightCm — высота одной строки ленточной формы;
ExtraHeightCm — запас на заголовок, кнопки, шапку таблицы, рамки и нижний отступ.
Например:
Высота строки = 0,529 см
Дополнительная высота = 4 см
Количество строк = 5
Высота формы = 5 × 0,529 + 4 = 6,645 см
2. Создай стандартный модуль
В Access:
Создание → Модуль
Сохрани модуль с именем:
mContinuousFormAutoHeight
Это должен быть обычный стандартный модуль, не модуль формы.
3. Вставь универсальный код модуля
В модуль mContinuousFormAutoHeight вставь полностью:
Option Compare Database
Option Explicit
' ============================================================
' УНИВЕРСАЛЬНЫЙ МОДУЛЬ АВТОПОДБОРА ВЫСОТЫ ЛЕНТОЧНОЙ ФОРМЫ
'
' Назначение:
' Автоматически изменяет высоту окна ленточной формы
' в зависимости от количества текущих записей.
'
' Основная формула:
'
' Высота = (N + ExtraRows) * RowHeightCm + ExtraHeightCm
'
' где:
' N - количество текущих записей формы с учётом фильтра;
' ExtraRows - дополнительное количество строк для расчёта;
' RowHeightCm - высота одной строки ленточной части в сантиметрах;
' ExtraHeightCm - дополнительная высота окна формы в сантиметрах.
'
' ------------------------------------------------------------
' Расшифровка параметров AutoFitContinuousFormHeight:
' ------------------------------------------------------------
'
' frm As Access.Form
' Форма, для которой выполняется автоподбор высоты.
' Обычно передаётся текущая форма:
'
' frm:=Me
'
'
' ExtraRows As Long
' Дополнительное количество строк, которое временно прибавляется
' к фактическому количеству записей.
'
' Примеры:
' ExtraRows:=0 - считать только сохранённые записи;
' ExtraRows:=1 - прибавить одну временную строку;
' ExtraRows:=-1 - уменьшить расчёт на одну строку.
'
' Основной сценарий:
' при начале добавления новой записи в ленточной форме
' вызываем автоподбор с ExtraRows:=1.
'
'
' RowHeightCm As Double
' Высота одной строки ленточной формы в сантиметрах.
'
' Если UseDetailSectionHeight:=True, то этот параметр можно не задавать:
' модуль сам возьмёт высоту строки из секции Detail формы.
'
' Если UseDetailSectionHeight:=False, тогда RowHeightCm нужно задать вручную.
'
' Пример:
' RowHeightCm:=0.529
'
'
' ExtraHeightCm As Double
' Дополнительная высота формы в сантиметрах.
'
' В неё обычно входит:
' - заголовок формы;
' - верхняя область с кнопками;
' - заголовки столбцов;
' - нижний отступ;
' - рамки окна;
' - небольшой запас по высоте.
'
' Пример:
' ExtraHeightCm:=4
'
'
' MinRows As Long
' Минимальное количество строк, которое форма должна учитывать.
'
' Пример:
' MinRows:=3
'
' Даже если записей 0 или 1, форма будет иметь высоту
' минимум как для 3 строк.
'
' Если MinRows:=0, ограничение не применяется.
'
'
' MaxRows As Long
' Максимальное количество строк, которое форма должна учитывать.
'
' Пример:
' MaxRows:=15
'
' Даже если записей 30, высота будет рассчитана максимум
' как для 15 строк.
'
' Если MaxRows:=0, ограничение не применяется.
'
' Важно:
' если используешь MaxRows, у формы лучше включить
' вертикальную полосу прокрутки.
'
'
' MinHeightCm As Double
' Минимальная итоговая высота окна формы в сантиметрах.
'
' Применяется уже после расчёта по строкам.
'
' Пример:
' MinHeightCm:=6
'
' Если расчётная высота меньше 6 см,
' будет применено 6 см.
'
' Если MinHeightCm:=0, ограничение не применяется.
'
'
' MaxHeightCm As Double
' Максимальная итоговая высота окна формы в сантиметрах.
'
' Применяется уже после расчёта по строкам.
'
' Пример:
' MaxHeightCm:=18
'
' Если расчётная высота больше 18 см,
' будет применено 18 см.
'
' Если MaxHeightCm:=0, ограничение не применяется.
'
'
' WidthCm As Double
' Ширина окна формы в сантиметрах.
'
' Если WidthCm > 0, процедура изменит не только высоту,
' но и ширину окна формы.
'
' Пример:
' WidthCm:=20
'
' Если WidthCm:=0, ширина формы не изменяется.
'
'
' IncludeNewRowIfAllowed As Boolean
' Учитывать ли стандартную пустую строку добавления Access.
'
' Если True и у формы AllowAdditions=True,
' к расчёту будет добавлена ещё одна строка.
'
' Если False, учитываются только фактические записи
' плюс значение ExtraRows.
'
' Обычно удобнее использовать:
' IncludeNewRowIfAllowed:=False
'
' А при начале добавления строки вручную вызывать:
' ExtraRows:=1
'
'
' UseDetailSectionHeight As Boolean
' Использовать ли фактическую высоту секции Detail формы
' как высоту одной строки.
'
' Если True:
' высота строки берётся автоматически из формы.
'
' Если False:
' используется значение RowHeightCm.
'
' Для универсального применения обычно удобно:
' UseDetailSectionHeight:=True
'
'
' ActivateFormBeforeResize As Boolean
' Активировать ли форму перед изменением размера окна.
'
' Обычно не требуется, если процедура вызывается из событий
' этой же формы.
'
' Рекомендуемое значение:
' ActivateFormBeforeResize:=False
'
' Можно поставить True, если процедура вызывается извне
' и есть риск, что активным является другое окно.
'
'
' PrintDebugInfo As Boolean
' Выводить ли отладочную информацию в Immediate Window VBA.
'
' Если True, в окно отладки будет выведено:
' - имя формы;
' - количество записей;
' - количество строк для расчёта;
' - высота строки;
' - дополнительная высота;
' - итоговая высота в см;
' - итоговая высота в твипах.
'
' Открыть Immediate Window можно сочетанием:
' Ctrl + G
'
'
' ShowErrors As Boolean
' Показывать ли сообщение об ошибке при сбое автоподбора высоты.
'
' Если True:
' пользователь увидит MsgBox с текстом ошибки.
'
' Если False:
' ошибка будет подавлена.
'
'
' ------------------------------------------------------------
' Важно:
' ------------------------------------------------------------
'
' DoCmd.MoveSize изменяет размер активного окна Access.
' Поэтому процедуру желательно вызывать из событий той формы,
' которую нужно изменить.
'
' Модуль рассчитан на отдельные формы, открытые как окно.
' Для подчинённых форм внутри другой формы этот способ напрямую
' не меняет размер элемента подчинённой формы.
'
' Размеры в DoCmd.MoveSize задаются в твипах.
' Внутри модуля сантиметры автоматически переводятся в твипы.
'
' 1 см примерно равен 567 твипам.
'
' ============================================================
' ------------------------------------------------------------
' Базовые настройки по умолчанию
' ------------------------------------------------------------
Private Const CFAH_TWIPS_PER_CM As Double = 567
Private Const CFAH_DEFAULT_ROW_HEIGHT_CM As Double = 0.529
Private Const CFAH_DEFAULT_EXTRA_HEIGHT_CM As Double = 4
' ------------------------------------------------------------
' Основная универсальная процедура автоподбора высоты формы
' ------------------------------------------------------------
Public Sub AutoFitContinuousFormHeight( _
ByVal frm As Access.Form, _
Optional ByVal ExtraRows As Long = 0, _
Optional ByVal RowHeightCm As Double = -1, _
Optional ByVal ExtraHeightCm As Double = -1, _
Optional ByVal MinRows As Long = 0, _
Optional ByVal MaxRows As Long = 0, _
Optional ByVal MinHeightCm As Double = 0, _
Optional ByVal MaxHeightCm As Double = 0, _
Optional ByVal WidthCm As Double = 0, _
Optional ByVal IncludeNewRowIfAllowed As Boolean = False, _
Optional ByVal UseDetailSectionHeight As Boolean = True, _
Optional ByVal ActivateFormBeforeResize As Boolean = False, _
Optional ByVal PrintDebugInfo As Boolean = False, _
Optional ByVal ShowErrors As Boolean = True)
On Error GoTo ErrHandler
Dim recordsCount As Long
Dim rowsCountForHeight As Long
Dim effectiveRowHeightCm As Double
Dim targetHeightCm As Double
Dim targetHeightTwips As Long
Dim targetWidthTwips As Long
' Если форма не передана — выходим
If frm Is Nothing Then Exit Sub
' Определяем высоту одной строки
If UseDetailSectionHeight = True Then
' Берём высоту секции Detail формы.
' Это самый универсальный вариант для ленточных форм.
effectiveRowHeightCm = TwipsToCm(frm.Section(acDetail).Height)
' Если по какой-то причине высота не определилась,
' используем ручной параметр или значение по умолчанию.
If effectiveRowHeightCm <= 0 Then
If RowHeightCm > 0 Then
effectiveRowHeightCm = RowHeightCm
Else
effectiveRowHeightCm = CFAH_DEFAULT_ROW_HEIGHT_CM
End If
End If
Else
' Используем высоту строки, переданную вручную
If RowHeightCm > 0 Then
effectiveRowHeightCm = RowHeightCm
Else
effectiveRowHeightCm = CFAH_DEFAULT_ROW_HEIGHT_CM
End If
End If
' Если дополнительная высота не задана — берём значение по умолчанию
If ExtraHeightCm < 0 Then
ExtraHeightCm = CFAH_DEFAULT_EXTRA_HEIGHT_CM
End If
' Получаем количество текущих записей формы с учётом фильтра
recordsCount = GetContinuousFormRecordsCount(frm)
' Основное количество строк для расчёта
rowsCountForHeight = recordsCount + ExtraRows
' Если нужно учитывать стандартную пустую строку добавления Access
If IncludeNewRowIfAllowed = True Then
If frm.AllowAdditions = True Then
rowsCountForHeight = rowsCountForHeight + 1
End If
End If
' Защита от отрицательного количества строк
If rowsCountForHeight < 0 Then
rowsCountForHeight = 0
End If
' Минимальное количество строк
If MinRows > 0 Then
If rowsCountForHeight < MinRows Then
rowsCountForHeight = MinRows
End If
End If
' Максимальное количество строк
If MaxRows > 0 Then
If rowsCountForHeight > MaxRows Then
rowsCountForHeight = MaxRows
End If
End If
' Считаем высоту в сантиметрах
targetHeightCm = rowsCountForHeight * effectiveRowHeightCm + ExtraHeightCm
' Минимальная высота формы в сантиметрах
If MinHeightCm > 0 Then
If targetHeightCm < MinHeightCm Then
targetHeightCm = MinHeightCm
End If
End If
' Максимальная высота формы в сантиметрах
If MaxHeightCm > 0 Then
If targetHeightCm > MaxHeightCm Then
targetHeightCm = MaxHeightCm
End If
End If
' Переводим высоту в твипы
targetHeightTwips = CmToTwips(targetHeightCm)
' Защита от нулевой высоты
If targetHeightTwips < 1 Then
targetHeightTwips = 1
End If
' При необходимости активируем форму перед изменением размера
If ActivateFormBeforeResize = True Then
DoCmd.SelectObject acForm, frm.Name, False
End If
' Если задана ширина — меняем и ширину, и высоту
If WidthCm > 0 Then
targetWidthTwips = CmToTwips(WidthCm)
DoCmd.MoveSize , , targetWidthTwips, targetHeightTwips
Else
' Если ширина не задана — меняем только высоту
DoCmd.MoveSize , , , targetHeightTwips
End If
' Отладочный вывод в Immediate Window
If PrintDebugInfo = True Then
Debug.Print "AutoFitContinuousFormHeight"
Debug.Print "Form: " & frm.Name
Debug.Print "Records: " & recordsCount
Debug.Print "ExtraRows: " & ExtraRows
Debug.Print "RowsForHeight: " & rowsCountForHeight
Debug.Print "UseDetailSectionHeight: " & UseDetailSectionHeight
Debug.Print "EffectiveRowHeightCm: " & effectiveRowHeightCm
Debug.Print "ExtraHeightCm: " & ExtraHeightCm
Debug.Print "TargetHeightCm: " & targetHeightCm
Debug.Print "TargetHeightTwips: " & targetHeightTwips
Debug.Print String(40, "-")
End If
Exit Sub
ErrHandler:
If ShowErrors = True Then
MsgBox "Ошибка при автоподборе высоты формы:" & vbCrLf & _
Err.Number & " - " & Err.Description, _
vbExclamation, "Автоподбор высоты формы"
End If
End Sub
' ------------------------------------------------------------
' Получение количества текущих записей формы
' Учитывает текущий фильтр формы
' ------------------------------------------------------------
Public Function GetContinuousFormRecordsCount(ByVal frm As Access.Form) As Long
On Error GoTo ErrHandler
Dim rs As Object
If frm Is Nothing Then
GetContinuousFormRecordsCount = 0
Exit Function
End If
Set rs = frm.RecordsetClone
' Если записей нет
If rs.BOF And rs.EOF Then
GetContinuousFormRecordsCount = 0
Else
' Для корректного RecordCount нужно перейти в конец набора
rs.MoveLast
GetContinuousFormRecordsCount = rs.RecordCount
End If
rs.Close
Set rs = Nothing
Exit Function
ErrHandler:
GetContinuousFormRecordsCount = 0
End Function
' ------------------------------------------------------------
' Перевод сантиметров в твипы
' ------------------------------------------------------------
Public Function CmToTwips(ByVal valueCm As Double) As Long
CmToTwips = CLng(valueCm * CFAH_TWIPS_PER_CM)
End Function
' ------------------------------------------------------------
' Перевод твипов в сантиметры
' Может пригодиться для отладки
' ------------------------------------------------------------
Public Function TwipsToCm(ByVal valueTwips As Long) As Double
TwipsToCm = valueTwips / CFAH_TWIPS_PER_CM
End Function
4. Как подключать к любой ленточной форме
В модуле конкретной формы нужно создать маленькую локальную процедуру-обёртку.
Например:
' ------------------------------------------------------------
' Локальная процедура автоподбора высоты текущей формы
' ------------------------------------------------------------
Private Sub FitCurrentFormHeight(Optional ByVal ExtraRows As Long = 0)
Call AutoFitContinuousFormHeight( _
frm:=Me, _
ExtraRows:=ExtraRows, _
RowHeightCm:=-1, _
ExtraHeightCm:=4, _
MinRows:=0, _
MaxRows:=0, _
MinHeightCm:=0, _
MaxHeightCm:=0, _
WidthCm:=0, _
IncludeNewRowIfAllowed:=False, _
UseDetailSectionHeight:=True, _
ActivateFormBeforeResize:=False, _
PrintDebugInfo:=False, _
ShowErrors:=True)
End Sub
В этом варианте:
UseDetailSectionHeight:=True
означает, что высота одной строки берётся автоматически из секции Detail / Область данных текущей формы.
Если нужно вручную задать высоту строки, например 0,529 см, тогда используй так:
Private Sub FitCurrentFormHeight(Optional ByVal ExtraRows As Long = 0)
Call AutoFitContinuousFormHeight( _
frm:=Me, _
ExtraRows:=ExtraRows, _
RowHeightCm:=0.529, _
ExtraHeightCm:=4, _
MinRows:=0, _
MaxRows:=0, _
MinHeightCm:=0, _
MaxHeightCm:=0, _
WidthCm:=0, _
IncludeNewRowIfAllowed:=False, _
UseDetailSectionHeight:=False, _
ActivateFormBeforeResize:=False, _
PrintDebugInfo:=False, _
ShowErrors:=True)
End Sub
5. Где вызывать автоподбор высоты
5.1. При загрузке формы
В модуле формы:
Private Sub Form_Load()
Call FitCurrentFormHeight
End Sub
5.2. После применения фильтра
Если на форме есть фильтрация, то после изменения фильтра вызывай:
Call FitCurrentFormHeight
Пример:
Private Sub ApplyCurrentFilter()
' Здесь пример фильтра.
' Условие фильтра нужно заменить на своё.
Me.Filter = "[Поле] = 'Значение'"
Me.FilterOn = True
' После фильтрации пересчитываем высоту формы
Call FitCurrentFormHeight
End Sub
5.3. При начале добавления новой записи
В событии формы Перед вставкой:
Private Sub Form_BeforeInsert(Cancel As Integer)
' Так как пользователь начал ввод новой строки,
' временно увеличиваем высоту формы на одну строку
Call FitCurrentFormHeight(1)
End Sub
Это нужно для того, чтобы форма расширялась сразу при появлении новой строки, а не только после сохранения записи.
5.4. После сохранения новой записи
В событии формы После вставки:
Private Sub Form_AfterInsert()
' После сохранения записи пересчитываем высоту уже по фактическому количеству строк
Call FitCurrentFormHeight
End Sub
5.5. После удаления записи
В событии формы После подтверждения удаления:
Private Sub Form_AfterDelConfirm(Status As Integer)
If Status = acDeleteOK Then
' После удаления строки пересчитываем высоту формы
Call FitCurrentFormHeight
End If
End Sub
Если удаление выполняется своей кнопкой через DELETE FROM ..., тогда после удаления тоже нужно вызвать:
Call FitCurrentFormHeight
или вызвать свою процедуру фильтрации, если она внутри уже делает автоподбор.
6. Рекомендуемые свойства формы
Для формы, к которой применяется автоподбор, желательно проверить:
Режим по умолчанию: Ленточная форма
Всплывающее окно: Да
Автоцентр: Да
Авторазмер: Нет
Разрешить добавление: Да/Нет — по задаче
Разрешить изменение: Да/Нет — по задаче
Разрешить удаление: Да/Нет — по задаче
Если используешь ограничения:
MaxRows:=10
или:
MaxHeightCm:=15
то лучше включить вертикальную прокрутку:
Полосы прокрутки: Вертикальная
Иначе часть строк может оказаться ниже видимой области.
7. Типовые настройки
Вариант 1. Полностью автоматическая высота строки
Private Sub FitCurrentFormHeight(Optional ByVal ExtraRows As Long = 0)
Call AutoFitContinuousFormHeight( _
frm:=Me, _
ExtraRows:=ExtraRows, _
ExtraHeightCm:=4, _
UseDetailSectionHeight:=True)
End Sub
Это самый универсальный вариант.
Вариант 2. Ручная высота строки
Private Sub FitCurrentFormHeight(Optional ByVal ExtraRows As Long = 0)
Call AutoFitContinuousFormHeight( _
frm:=Me, _
ExtraRows:=ExtraRows, _
RowHeightCm:=0.529, _
ExtraHeightCm:=4, _
UseDetailSectionHeight:=False)
End Sub
Такой вариант удобен, если ты заранее измерил высоту строки.
Вариант 3. Ограничить максимальную высоту
Private Sub FitCurrentFormHeight(Optional ByVal ExtraRows As Long = 0)
Call AutoFitContinuousFormHeight( _
frm:=Me, _
ExtraRows:=ExtraRows, _
ExtraHeightCm:=4, _
MaxRows:=15, _
MaxHeightCm:=18, _
UseDetailSectionHeight:=True)
End Sub
Форма не будет расти бесконечно вверх/вниз, даже если записей много.
Вариант 4. Задать и высоту, и ширину формы
Private Sub FitCurrentFormHeight(Optional ByVal ExtraRows As Long = 0)
Call AutoFitContinuousFormHeight( _
frm:=Me, _
ExtraRows:=ExtraRows, _
ExtraHeightCm:=4, _
WidthCm:=22, _
UseDetailSectionHeight:=True)
End Sub
Если WidthCm:=0, ширина не меняется.
8. Минимальный комплект кода в каждой форме
Для каждой новой ленточной формы достаточно вставить в её модуль:
Private Sub Form_Load()
Call FitCurrentFormHeight
End Sub
Private Sub Form_BeforeInsert(Cancel As Integer)
Call FitCurrentFormHeight(1)
End Sub
Private Sub Form_AfterInsert()
Call FitCurrentFormHeight
End Sub
Private Sub Form_AfterDelConfirm(Status As Integer)
If Status = acDeleteOK Then
Call FitCurrentFormHeight
End If
End Sub
Private Sub FitCurrentFormHeight(Optional ByVal ExtraRows As Long = 0)
Call AutoFitContinuousFormHeight( _
frm:=Me, _
ExtraRows:=ExtraRows, _
ExtraHeightCm:=4, _
UseDetailSectionHeight:=True)
End Sub
А если в форме есть фильтрация, после применения фильтра добавляешь:
Call FitCurrentFormHeight
9. Проверка работы
После подключения:
1. Открой ленточную форму.
2. Проверь, что высота окна стала соответствовать количеству строк.
3. Примени фильтр, если он есть.
4. Проверь, что высота пересчиталась после фильтрации.
5. Начни вводить новую строку.
6. Форма должна временно увеличиться на одну строку.
7. Сохрани новую строку.
8. Высота должна пересчитаться по фактическому количеству записей.
9. Удали строку.
10. Высота должна уменьшиться.
10. Практическое правило настройки
Сначала пробуй так:
UseDetailSectionHeight:=True
ExtraHeightCm:=4
Если форма получается слишком низкой — увеличивай:
ExtraHeightCm:=4.5
или:
ExtraHeightCm:=5
Если слишком высокой — уменьшай:
ExtraHeightCm:=3.5
То есть в большинстве случаев настраивать придётся только один параметр:
ExtraHeightCm