При разработке сложных интерфейсов в 1С:Предприятие 8.3 часто возникает задача динамического построения форм. Например, когда набор полей зависит от настроек пользователя, выбранного вида операции или внешних данных. Стандартный конфигуратор позволяет создать статичную форму, но что делать, если нам нужно добавить колонку в таблицу или поле ввода прямо в момент работы программы?
В этой статье мы подробно разберем механизм программной модификации управляемой формы. Мы рассмотрим, как правильно создавать реквизиты (данные) и элементы (визуальное отображение), избегая распространенных ошибок.
Прежде чем приступать к написанию кода, давайте разберем логику работы платформы. Управляемая форма четко разделяет данные (Реквизиты формы) и интерфейс (Элементы формы).
Нельзя просто "добавить поле ввода". Сначала мы должны сказать форме, что у нее есть новая переменная для хранения данных, и только потом создать визуальный элемент, который будет эту переменную отображать. Весь процесс можно разделить на три ключевых этапа:
РеквизитФормы.ИзменитьРеквизиты.Элементы.Рассмотрим каждый этап подробнее на практическом примере.
В 1С изменение структуры данных формы — это ресурсоемкая операция. Поэтому платформа требует, чтобы мы добавляли (или удаляли) реквизиты "пакетом". Для этого используется массив. Давайте создадим новый строковый реквизит "КомментарийМенеджера".
Сначала определим тип будущего реквизита. Для этого используем конструктор ОписаниеТипов.
// Описываем тип данных: Строка длиной 100 символов
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Строка"));
КвалификаторыСтроки = Новый КвалификаторыСтроки(100);
ОписаниеТипаСтрока = Новый ОписаниеТипов(МассивТипов, , КвалификаторыСтроки);
Теперь создадим объект РеквизитФормы. Обратите внимание, что это еще не добавление в форму, а только описание намерения.
// Создаем описание нового реквизита
// Параметры: Имя, Тип, Путь, Заголовок, СохраняемыеДанные
НовыйРеквизит = Новый РеквизитФормы(
"НашНовыйРеквизит", // Имя реквизита (для обращения в коде)
ОписаниеТипаСтрока, // Тип данных
"", // Путь (если добавляем в корень формы - пустая строка)
"Комментарий менеджера" // Заголовок
);
Если нам нужно добавить реквизит не в саму форму, а, например, как колонку в Таблицу Значений, размещенную на форме, третий параметр (Путь) должен содержать имя этой таблицы. Например: "Объект.Товары".
Теперь поместим наш реквизит в массив, так как метод изменения реквизитов принимает именно список.
ДобавляемыеРеквизиты = Новый Массив;
ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
Это самый важный момент. Мы должны вызвать метод формы ИзменитьРеквизиты. Этот метод доступен только на сервере. Он принимает два массива: список добавляемых реквизитов и список удаляемых (путей к ним).
// Изменяем структуру формы
// Первым параметром передаем массив добавляемых, вторым - удаляемых (сейчас он пуст)
ЭтаФорма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
Важно: После выполнения этой команды объект формы на сервере пересоздается. Все ссылки на старые данные могут быть потеряны, если не быть осторожным, но добавленные реквизиты становятся полноправными членами коллекции ЭтаФорма.
Теперь, когда у нас есть место для хранения данных (реквизит "НашНовыйРеквизит"), мы можем создать поле ввода для пользователя. Работа с элементами также происходит на сервере.
Используем коллекцию ЭтаФорма.Элементы и ее метод Добавить.
// Добавляем элемент на форму
// Параметры: Имя элемента, Тип элемента, Родитель (группа или сама форма)
НовыйЭлемент = Элементы.Добавить(
"ПолеНашНовыйРеквизит", // Уникальное имя элемента интерфейса
Тип("ПолеФормы"), // Мы хотим поле ввода
ЭтаФорма // Родитель - корень формы
);
Элемент создан, но он "пустой". Нам нужно связать его с данными и настроить внешний вид. Самое главное свойство здесь — ПутьКДанным. Оно должно в точности совпадать с именем реквизита, который мы создали на первом этапе.
// Настраиваем свойства элемента
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; // Указываем, что это поле ввода, а не надпись
НовыйЭлемент.ПутьКДанным = "НашНовыйРеквизит"; // Связь с данными!
НовыйЭлемент.Заголовок = "Введите комментарий";
Теперь при открытии или обновлении формы пользователь увидит новое поле, и данные, введенные в него, будут доступны в коде через ЭтаФорма.НашНовыйРеквизит.
Давайте рассмотрим более сложный и частый сценарий. Предположим, у нас на форме есть таблица (динамический список или таблица значений) с именем ТаблицаТовары. Мы хотим программно добавить в неё колонку "ДопАртикул".
Проанализируем полный код процедуры, которая должна выполняться на сервере:
&НаСервере
Процедура ДобавитьКолонкуПрограммно()
// 1. Описываем тип (например, Число 10, 0)
КЧ = Новый КвалификаторыЧисла(10, 0);
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТипов, , КЧ);
// 2. Создаем реквизит.
// ВАЖНО: Путь указываем как "ТаблицаТовары", чтобы колонка попала внутрь таблицы
НовыйРеквизит = Новый РеквизитФормы(
"ДопАртикул",
ОписаниеТипаЧисло,
"ТаблицаТовары",
"Дополнительный артикул"
);
МассивРеквизитов = Новый Массив;
МассивРеквизитов.Добавить(НовыйРеквизит);
// 3. Применяем изменения
// Обязательно оборачиваем в Попытку, так как реквизит может уже существовать
Попытка
ЭтаФорма.ИзменитьРеквизиты(МассивРеквизитов);
Исключение
// Реквизит уже есть или ошибка типа
Сообщить("Ошибка добавления реквизита: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// 4. Создаем элемент формы
// Родитель теперь не ЭтаФорма, а элемент таблицы
РодительДляКолонки = Элементы.ТаблицаТовары;
НовыйЭлемент = Элементы.Добавить(
"КолонкаДопАртикул",
Тип("ПолеФормы"),
РодительДляКолонки
);
// 5. Настраиваем
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
// Путь к данным строится через точку от имени таблицы
НовыйЭлемент.ПутьКДанным = "ТаблицаТовары.ДопАртикул";
КонецПроцедуры
В процессе работы с программным изменением форм нужно учитывать несколько нюансов:
ИзменитьРеквизиты доступен только на сервере. В процедуре &НаСервереБезКонтекста он недоступен, так как там нет объекта формы.ИзменитьРеквизиты в циклах. Сначала сформируйте полный массив всех нужных реквизитов, и добавьте их одним вызовом.ЭтаФорма.ИзменитьРеквизиты( , МассивУдаляемыхРеквизитов). В массиве удаляемых передаются строки — пути к реквизитам.Помимо полей ввода, мы часто создаем группы для организации интерфейса (вкладки, сворачиваемые группы). Для этого используется тот же метод Элементы.Добавить, но с типом ГруппаФормы.
// Создаем группу (папку) на форме
НоваяГруппа = Элементы.Добавить("ГруппаНастройки", Тип("ГруппаФормы"), ЭтаФорма);
НоваяГруппа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
НоваяГруппа.Заголовок = "Дополнительные настройки";
НоваяГруппа.Отображение = ОтображениеОбычнойГруппы.СлабоеВыделение;
// Теперь добавляем элементы ВНУТРЬ этой группы
ЭлементВГруппе = Элементы.Добавить("ФлажокАктивности", Тип("ПолеФормы"), НоваяГруппа);
Таким образом, комбинируя создание реквизитов и элементов групп, можно строить полностью динамические интерфейсы, адаптирующиеся под нужды конкретного пользователя или бизнес-процесса.
Используйте этот подход аккуратно. Чрезмерное усложнение программного кода формы может затруднить её дальнейшую поддержку и отладку. Всегда комментируйте блоки программного создания элементов, чтобы другие разработчики понимали, откуда на форме берутся поля, которых нет в конфигураторе.