При разработке сложных интерфейсов на управляемых формах программисты часто сталкиваются с необходимостью динамического создания элементов — для этого есть инструмент компоновки адаптивных интерфейсов 1С. Одной из типичных задач является создание «сетки» из нескольких групп одинаковой ширины. Однако попытка вычислить ширину формы в процедуре ПриСозданииНаСервере() неизменно приводит к результату 0. В этой статье мы разберем, почему так происходит, и какими методами можно добиться желаемого результата в интерфейсе «Такси».
Разберем причину этой ситуации. Управляемая форма в 1С строится на декларативном принципе. Это означает, что на сервере мы описываем только структуру формы: какие на ней будут группы, поля и кнопки. Само же «рисование» (рендеринг) формы происходит исключительно на стороне клиента (в тонком, толстом или веб-клиенте).
Проанализируем цепочку событий: когда выполняется код ПриСозданииНаСервере(), формы как графического объекта еще не существует. Платформа еще не знает, на мониторе какого разрешения будет открыто окно, какой масштаб установлен у пользователя и каков размер родительского окна. Именно поэтому любые попытки прочитать свойство Ширина у программно созданных элементов или у самой формы возвращают 0. Данные о физических размерах появляются только после того, как форма была полностью сформирована и отображена на экране клиента.
Рассмотрим самый правильный и «нативный» для управляемого интерфейса способ. В 1С 8.3 свойство Ширина (Width) для элементов, у которых установлено РастягиватьПоГоризонтали = Истина, работает не как фиксированный размер в пикселях, а как коэффициент (или вес) относительно соседних элементов в той же группе.
Представим, что нам нужно создать горизонтальную группу и в ней три подгруппы равной ширины. Нам не нужно знать ширину экрана. Мы можем поступить следующим образом:
Горизонтальная.Ширина в одинаковое ненулевое значение (например, 10).РастягиватьПоГоризонтали в значение Истина.Посмотрим на пример кода:
&НаСервере
Процедура СоздатьСеткуЭлементовНаСервере()
// Создаем общую горизонтальную группу-контейнер
НоваяГруппа = Элементы.Добавить("ГруппаКонтейнер", Тип("ГруппаФормы"), ЭтаФорма);
НоваяГруппа.Вид = ВидГруппыФормы.БезОтображения;
НоваяГруппа.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Горизонтальная;
// Добавляем N подгрупп
Для Итератор = 1 По 3 Цикл
ИмяПодгруппы = "Подгруппа_" + Итератор;
Подгруппа = Элементы.Добавить(ИмяПодгруппы, Тип("ГруппаФормы"), НоваяГруппа);
Подгруппа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
// Устанавливаем одинаковую "ширину-вес"
Подгруппа.Ширина = 10;
Подгруппа.РастягиватьПоГоризонтали = Истина;
КонецЦикла;
КонецПроцедуры
В данном случае платформа видит, что общая ширина контейнера должна быть распределена между тремя элементами с одинаковым весом (10, 10 и 10). Система автоматически вычислит их реальный размер, исходя из доступного пространства на экране. Это идеальный способ для адаптивного интерфейса «Такси».
Если специфика задачи все же требует знания физических параметров монитора (например, для принятия решения о количестве колонок), мы можем получить информацию об экранах на стороне клиента. Выясним, как передать эти данные на сервер еще до момента создания формы.
Разберем алгоритм действий:
ОткрытьФорму() на клиенте вызываем метод ПолучитьИнформациюЭкрановКлиента().Параметры метода ОткрытьФорму().ПриСозданииНаСервере() считываем переданный параметр.Пример реализации вызова:
&НаКлиенте
Процедура ОткрытьФормуСПараметрамиЭкрана()
ИнфоЭкранов = ПолучитьИнформациюЭкрановКлиента();
// Берем параметры первого (основного) монитора
ШиринаЭкрана = ИнфоЭкранов[0].ШиринаРабочейОбласти;
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("ШиринаМонитора", ШиринаЭкрана);
ОткрытьФорму("ОбщаяФорма.НашаДинамическаяФорма", ПараметрыФормы);
КонецПроцедуры
Теперь на сервере мы можем использовать эти данные:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ШиринаМонитора") Тогда
// Теперь мы знаем примерную ширину и можем рассчитать
// количество колонок или задать жесткие размеры
ШиринаДляЭлементов = Параметры.ШиринаМонитора / 4;
КонецЕсли;
КонецПроцедуры
Важный нюанс: учитывайте коэффициент масштабирования (Scale). В интерфейсе «Такси» пользователи часто используют масштаб 125% или 150%. Метод ПолучитьИнформациюЭкрановКлиента() возвращает также и коэффициент масштаба, который необходимо учитывать в расчетах.
Иногда форма может менять размер уже после открытия (пользователь растянул окно). В управляемых формах нет прямого события «ИзменениеРазмераОкна». Чтобы обойти это ограничение, можно использовать обработчик ожидания.
Рассмотрим этот метод. Мы можем запустить периодическую проверку ширины формы на клиенте и, если она существенно изменилась, пересчитывать параметры элементов.
ПриОткрытии() подключаем обработчик.ЭтаФорма.Ширина (на клиенте после открытия она уже заполнена).
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ПроверитьРазмерФормы", 1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьРазмерФормы()
ТекущаяШирина = ЭтаФорма.Ширина;
// Логика корректировки элементов
КонецПроцедуры
Однако стоит помнить, что частое программное изменение структуры формы (удаление и добавление элементов) — операция ресурсоемкая. Старайтесь ограничиваться изменением свойств Видимость или Ширина существующих элементов.
Проанализируем встроенные возможности платформы. Чтобы интерфейс выглядел современно и не ломался при малых разрешениях, используйте свойство групп Группировка = ГоризонтальнаяЕслиВозможно.
Это позволит системе вести себя следующим образом: если места на экране достаточно, группы выстроятся в один ряд (как и хотел автор вопроса). Если же пользователь откроет форму на маленьком экране или в режиме «в пол-экрана», платформа сама перенесет подгруппы на новые строки, предотвращая появление горизонтальной прокрутки.
Подведем итог: для решения задачи «одинаковой ширины» лучше всего подходит использование свойства Ширина как весового коэффициента вместе с РастягиватьПоГоризонтали = Истина. Это избавляет от необходимости сложных расчетов пикселей и учитывает специфику работы тонкого клиента.