В виду развития механизмов расширений, тема может быть немного холиварной, некоторые считают, что программное изменение форм больше не нужно, другие, наоборот, являются адептами программной модификации форм.
Я отношусь к адептам программной модификации форм и считаю это лучшим способом сократить количество анальной боли при обновлении конфигураций.
А ещё программная генерация элементов форм позволяет сделать, например, динамическое количество колонок (как в документах установки цен).
В моменте, делать изменения в визуальном интерфейсе намного проще, подтянул форму в расширение, поправил что нужно и радуешься. Но потом происходит обновление, и как то может начаться сильнейшая грусть от того, что разработчики поменяли названия групп, переименовали что-нибудь. А при попытке смерджить почти обязательно отвалится какая-нибудь очень важная фиговина.
Чтобы минимизировать последствия от обновления, формы нужно дорабатывать кодом.
Модификацию формы привязываем к событию “ПриСозданииНаСервере”
Группа формы
1 2 3 4 5 6 7 |
НовыйЭлемент = Элементы.Добавить("МОД_СвернутаяГруппа",Тип("ГруппаФормы"), Неопределено); // Неопределенно - означает добавление конец корневой группы формы НовыйЭлемент.Вид = ВидГруппыФормы.ОбычнаяГруппа; НовыйЭлемент.Заголовок = "Группа реквизитов объекта"; НовыйЭлемент.Отображение = ОтображениеОбычнойГруппы.Нет; НовыйЭлемент.ОтображениеУправления = ОтображениеУправленияОбычнойГруппы.Картинка; НовыйЭлемент.Поведение = ПоведениеОбычнойГруппы.Свертываемая; НовыйЭлемент.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная; |
Так мы создадим группу на форме. Обратите внимание, что при создании нового элемента формы, мы не можем делать с ним сразу все операции. Например сделать группу сразу свернутой не можно через Элементы
1 |
Элементы.МОД_СвернутаяГруппа.Скрыть(); // Свернём созданную группу |
Таким образом мы создали сразу свернутую группу. В которую в дальнейшем мы положим наши реквизиты объекта.
Реквизиты объекта
Начнем с самого простого. Мы создали реквизит в документе, справочнике, обработке или ещё где нибудь и хотим разместить его на форме, в надежде, что он будет сохраняться при записи нашего объекта.
Для примера возьмем реквизит Контрагент и разместим его в группе “МОД_СвернутаяГруппа”
1 2 3 4 5 6 7 |
НовыйЭлемент = Элементы.Добавить("МОД_КонтрагентВСвернутойГруппе", Тип("ПолеФормы"), Элементы.МОД_СвернутаяГруппа); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.АвтоОтметкаНезаполненного = Истина; НовыйЭлемент.Заголовок = "Контрагент в свернутой группе"; НовыйЭлемент.ПутьКДанным = "Объект.Контрагент"; НовыйЭлемент.УстановитьДействие("ПриИзменении", "МОД_КонтрагентПриИзменении"); // МОД_НоменклатураПриИзменении - это клиентская процедура, которая будет обрабатывать событие при изменении |

Табличная часть
Хотел бы отметить, что здесь идет речь о табличных частях. Про программную модификацию динамических списков есть отдельная статья.
Вот мы и добрались до табличной части. В принципе такая же самые принципы. Главное отличие в том, что нам сначала нужно вывести таблицу формы, а затем привязать к этой таблице колонки. Просто обращаем внимание на поле “ПутьКДанным”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// ТАБЛИЧНАЯ ЧАСТЬ НовыйЭлемент = Элементы.Добавить("МОД_ГрафикПлатежей", Тип("ТаблицаФормы"), Элементы.МОД_СвернутаяГруппа); НовыйЭлемент.ПутьКДанным = "Объект.ГрафикПлатежей"; НовыйЭлемент.Отображение = ОтображениеТаблицы.Список; НовыйЭлемент.УстановитьДействие("Выбор", "МОД_ГрафикПлатежейВыбор"); // ДАТА НовыйЭлемент = Элементы.Добавить("МОД_ГрафикПлатежейДата", Тип("ПолеФормы"), Элементы.МОД_ГрафикПлатежей); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.Заголовок = "Дата"; НовыйЭлемент.ПутьКДанным = "Объект.ГрафикПлатежей.Дата"; // СУММА НовыйЭлемент = Элементы.Добавить("МОД_ГрафикПлатежейСумма", Тип("ПолеФормы"), Элементы.МОД_ГрафикПлатежей); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.Заголовок = "Сумма"; НовыйЭлемент.ПутьКДанным = "Объект.ГрафикПлатежей.Сумма"; НовыйЭлемент.УстановитьДействие("ПриИзменении", "МОД_ГрафикПлатежейСуммаПриИзменении"); |
Но табличная часть получилась уродской. Дата в одном конце таблицы, сумма в другом…

Для красоты выведем заглушку в нашу табличную часть
1 2 3 4 5 6 7 8 9 |
// ЗАГЛУШКА (для красивого вида таблицы) МассивДобавляемыхРеквизитов = Новый Массив; МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("МОД_ГрафикПлатежейЗаглушка", Новый ОписаниеТипов("Строка"), "Объект.ГрафикПлатежей", " ")); ИзменитьРеквизиты(МассивДобавляемыхРеквизитов); НовыйЭлемент = Элементы.Добавить("МОД_ГрафикПлатежейЗаглушка", Тип("ПолеФормы"), Элементы.МОД_ГрафикПлатежей); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.Доступность = Ложь; НовыйЭлемент.ПутьКДанным = "Объект.ГрафикПлатежей.МОД_ГрафикПлатежейЗаглушка"; |

Красивее же?!
Реквизиты формы
При добавлении заглушки мы подобрались к программному созданию реквизитов формы. То есть тех реквизитов, которые мы не хотим сохранять автоматически в объекте (документе или справочнике). Но хотим выводить в них какие то служебные данные и не только.
Давайте сразу сделаем таблицу значений.
Я обычно подготавливаю необходимые типы данных, которые буду использовать в дальнейшем. Во первых они довольно длинные, во вторых они часто повторяющиеся.
1 2 3 4 5 6 7 8 9 10 11 12 |
// Описание квалификторов КвалификаторЧислоНеотрицательное15_2 = Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Неотрицательный); КвалификаторЧислоНеотрицательное10_3 = Новый КвалификаторыЧисла(10, 3, ДопустимыйЗнак.Неотрицательный); КвалификаторСтрока100 = Новый КвалификаторыСтроки(100, ДопустимаяДлина.Переменная); // Описание типов ТипКонтрагентСсылка = Новый ОписаниеТипов("СправочникСсылка.Номенклатура"); ТипНоменклатураСсылка = Новый ОписаниеТипов("СправочникСсылка.Номенклатура"); ТипТаблицаЗначений = Новый ОписаниеТипов("ТаблицаЗначений"); ТипЧислоНеотрицательное15_2 = Новый ОписаниеТипов("Число",,,КвалификаторЧислоНеотрицательное15_2); ТипЧислоНеотрицательное10_3 = Новый ОписаниеТипов("Число",,,КвалификаторЧислоНеотрицательное10_3); ТипСтрока100 = Новый ОписаниеТипов("Строка",,,,КвалификаторСтрока100); |
Далее добавляем реквизиты на формы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
МассивДобавляемыхРеквизитов = Новый Массив; //Реквизит формы МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("МОД_Контрагент", ТипКонтрагентСсылка,,"Контрагент")); // Табличная часть МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("МОД_ТаблицаТоваров", ТипТаблицаЗначений)); // Колонки табличной части МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Номенклатура", ТипНоменклатураСсылка, "МОД_ТаблицаТоваров")); МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Количество", ТипЧислоНеотрицательное10_3, "МОД_ТаблицаТоваров")); МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Цена", ТипЧислоНеотрицательное15_2, "МОД_ТаблицаТоваров")); МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Сумма", ТипЧислоНеотрицательное15_2, "МОД_ТаблицаТоваров")); МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Комментарий", ТипСтрока100, "МОД_ТаблицаТоваров")); ИзменитьРеквизиты(МассивДобавляемыхРеквизитов); |
А теперь генерируем элементы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
// 1. Добавим группу в которую будем складывать реквизиты формы НовыйЭлемент = Элементы.Добавить("МОД_ГруппаТаблицаЗначений",Тип("ГруппаФормы"), Неопределено); НовыйЭлемент.Вид = ВидГруппыФормы.ОбычнаяГруппа; НовыйЭлемент.Заголовок = "Группа таблица значений"; НовыйЭлемент.Отображение = ОтображениеОбычнойГруппы.Нет; НовыйЭлемент.ОтображениеУправления = ОтображениеУправленияОбычнойГруппы.ГиперссылкаЗаголовка; НовыйЭлемент.Поведение = ПоведениеОбычнойГруппы.Свертываемая; НовыйЭлемент.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная; // 2. Сделаем группу свернутой Элементы.МОД_ГруппаТаблицаЗначений.Скрыть(); Элементы.МОД_ГруппаТаблицаЗначений.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная; // 3. Выведем реквизит контрагент НовыйЭлемент = Элементы.Добавить("МОД_Контрагент", Тип("ПолеФормы"), Элементы.МОД_ГруппаТаблицаЗначений); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.АвтоОтметкаНезаполненного = Истина; НовыйЭлемент.Заголовок = "Контрагент"; НовыйЭлемент.ПутьКДанным = "МОД_Контрагент"; // имя реквизита формы НовыйЭлемент.УстановитьДействие("ПриИзменении", "МОД_КонтрагентПриИзменении"); // 4. Выведем таблицу значений НовыйЭлемент = Элементы.Добавить("МОД_ТаблицаТоваров", Тип("ТаблицаФормы"), Элементы.МОД_ГруппаТаблицаЗначений); НовыйЭлемент.ПутьКДанным = "МОД_ТаблицаТоваров"; НовыйЭлемент.Отображение = ОтображениеТаблицы.Список; НовыйЭлемент.УстановитьДействие("Выбор", "МОД_ТаблицаТоваровВыбор"); // 5. Выведем колонки // НОМЕНКЛАТУРА НовыйЭлемент = Элементы.Добавить("МОД_ТаблицаТоваровНоменклатура", Тип("ПолеФормы"), Элементы.МОД_ТаблицаТоваров); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.Заголовок = "Номенклатура"; НовыйЭлемент.ПутьКДанным = "МОД_ТаблицаТоваров.Номенклатура"; // КОЛИЧЕСТВО НовыйЭлемент = Элементы.Добавить("МОД_ТаблицаТоваровКоличество", Тип("ПолеФормы"), Элементы.МОД_ТаблицаТоваров); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.Заголовок = "Кол-во"; НовыйЭлемент.ПутьКДанным = "МОД_ТаблицаТоваров.Количество"; НовыйЭлемент.УстановитьДействие("ПриИзменении", "МОД_КоличествоПриИзменении"); // ЦЕНА НовыйЭлемент = Элементы.Добавить("МОД_ТаблицаТоваровЦена", Тип("ПолеФормы"), Элементы.МОД_ТаблицаТоваров); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.Заголовок = "Цена"; НовыйЭлемент.ПутьКДанным = "МОД_ТаблицаТоваров.Цена"; НовыйЭлемент.УстановитьДействие("ПриИзменении", "МОД_ЦенаПриИзменении"); // СУММА НовыйЭлемент = Элементы.Добавить("МОД_ТаблицаТоваровСумма", Тип("ПолеФормы"), Элементы.МОД_ТаблицаТоваров); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.ТолькоПросмотр = Истина; НовыйЭлемент.Заголовок = "Сумма"; НовыйЭлемент.ПутьКДанным = "МОД_ТаблицаТоваров.Сумма"; // КОММЕНТАРИЙ НовыйЭлемент = Элементы.Добавить("МОД_ТаблицаТоваровКомментарий", Тип("ПолеФормы"), Элементы.МОД_ТаблицаТоваров); НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода; НовыйЭлемент.Заголовок = "Комментарий"; НовыйЭлемент.ПутьКДанным = "МОД_ТаблицаТоваров.Комментарий"; |

Ну вроде выглядит неплохо.
Кнопки
Добавление кнопки делается в 3 этапа
- Создание команды
- Размещение команды на форме
- Определение обработчика команды
1 2 3 4 5 6 7 8 9 |
// 1. Создание команды НоваяКоманда = ЭтаФорма.Команды.Добавить("МОД_КомандаСказатьПривет"); НоваяКоманда.Заголовок = "Сказать привет!"; НоваяКоманда.Действие = "МОД_КомандаСказатьПривет"; //Имя клиентской процедуры // 2. Размещение в командной панели НовыйЭлемент = ЭтаФорма.Элементы.Добавить("КнопкаСказатьПривет", Тип("КнопкаФормы"), Элементы.ФормаКоманднаяПанель); НовыйЭлемент.Заголовок = "Привет!"; НовыйЭлемент.ИмяКоманды = "МОД_КомандаСказатьПривет"; |
Обработчик команды – это просто клиентская процедура в модуле формы.
1 2 3 4 5 6 7 8 |
&НаКлиенте Процедура МОД_КомандаСказатьПривет(Команда) Сообщение = Новый СообщениеПользователю; Сообщение.Текст = "Привет, программист!"; Сообщение.Сообщить(); КонецПроцедуры |

Меню и подменю
Кнопку мы добавляли в стандартную командную панель формы. Подобным образом можно размещать в командные панели динамического списка, табличной части итп. Но только вместо Элементы.ФормаКоманднаяПанель, было бы Элементы.ИмяТабличнойЧасти.КоманднаяПанель
Но давайте накодим свою командную панель с кнопками и подменю.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// Добавим в корень формы командную панель НоваяКоманднаяПанель = Элементы.Добавить("МОД_КоманднаяПанельСПодменю", Тип("ГруппаФормы"), Неопределено); НоваяКоманднаяПанель.Вид = ВидГруппыФормы.КоманднаяПанель; // Разместим кнопку в командной панели НоваяКоманда = Команды.Добавить("МОД_КомандаСказатьХеллоу"); НоваяКоманда.Заголовок = "Хеллоу"; НоваяКоманда.Действие = "МОД_КомандаСказатьХеллоу"; //Имя клиентской процедуры НовыйЭлемент = Элементы.Добавить("МОД_КнопкаСказатьХеллоу", Тип("КнопкаФормы"), Элементы.МОД_КоманднаяПанельСПодменю); //НовыйЭлемент.Заголовок = "Хеллоу"; У кнопки не обязательно переопределять заголовок, если он уже установлен в заголовке команды НовыйЭлемент.ИмяКоманды = "МОД_КомандаСказатьХеллоу"; // Добавим подменю в командную панель НовоеПодменю = Элементы.Добавить("МОД_ПодменюКоманднаяПанель", Тип("ГруппаФормы"), Элементы.МОД_КоманднаяПанельСПодменю); НовоеПодменю.Заголовок = "Это заголовок подменю"; НовоеПодменю.Вид = ВидГруппыФормы.Подменю; // Разместим кнопку №1 в подменю НоваяКоманда = Команды.Добавить("МОД_КомандаСказатьКоманднаяПанель"); НоваяКоманда.Заголовок = "Сказать командная панель"; НоваяКоманда.Действие = "МОД_КомандаСказатьКоманднаяПанель"; //Имя клиентской процедуры НовыйЭлемент = Элементы.Добавить("МОД_КнопкаСказатьКоманднаяПанель", Тип("КнопкаФормы"), Элементы.МОД_ПодменюКоманднаяПанель); НовыйЭлемент.Заголовок = "Сказать командная панель"; НовыйЭлемент.ИмяКоманды = "МОД_КомандаСказатьКоманднаяПанель"; // Разместим кнопку №2 в подменю НоваяКоманда = Команды.Добавить("МОД_КомандаСказатьПока"); НоваяКоманда.Заголовок = "Сказать пока"; НоваяКоманда.Действие = "МОД_КомандаСказатьПока"; //Имя клиентской процедуры НовыйЭлемент = Элементы.Добавить("МОД_КнопкаСказатьПока", Тип("КнопкаФормы"), Элементы.МОД_ПодменюКоманднаяПанель); НовыйЭлемент.Заголовок = "Сказать пока"; НовыйЭлемент.ИмяКоманды = "МОД_КомандаСказатьПока"; |
Получаем такой результат:

Как добавить элемент не в конец группы, а в середину
Всё это время, мы пользовались методом Элементы.Добавить(<Имя>, <ТипЭлемента>, <Родитель>), который добавляем новый элемент в конец родительской группы, если указан родитель и в конец формы, если в родителя передаем Неопределено.
Существует метод Элементы.Вставить(<Имя>, <ТипЭлемента>, <Родитель>, <Элемент>), где последним параметром передается элемент родительской группы, ПЕРЕД КОТОРЫМ мы хотим добавить наш вновь созданный элемент.
Представим, что мы хотим вставить новую кнопку между уже существующими

1 2 3 4 5 6 7 8 |
// Вставим кнопку №3 между кнопкой №1 и №2 НоваяКоманда = Команды.Добавить("МОД_КомандаНомерТри"); НоваяКоманда.Заголовок = "Сказать пока"; НоваяКоманда.Действие = "МОД_КомандаНомерТри"; //Имя клиентской процедуры НовыйЭлемент = Элементы.Вставить("МОД_КнопкаМеждуПервойИВторой", Тип("КнопкаФормы"), Элементы.МОД_ПодменюКоманднаяПанель, Элементы.МОД_КнопкаСказатьПока); НовыйЭлемент.Заголовок = "Кнопка между"; НовыйЭлемент.ИмяКоманды = "МОД_КомандаНомерТри"; |
Результат:

Всё тоже самое можно использовать для других элементов на форме.
Страницы
Это вообще easy peasy lemon squeezy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// 1. Создадим группу страницы, чтобы размещать на ней непосредственно страницы СтраницыФормы = Элементы.Добавить("МОД_СтраницыФормы", Тип("ГруппаФормы"), Неопределено); СтраницыФормы.Вид = ВидГруппыФормы.Страницы; // 2. Страница 1 НоваяСтраница = Элементы.Добавить("МОД_Страница1", Тип("ГруппаФормы"), Элементы.МОД_СтраницыФормы); НоваяСтраница.Вид = ВидГруппыФормы.Страница; НоваяСтраница.Заголовок = "Страница 1"; // 3. Страница 1 НоваяСтраница = Элементы.Добавить("МОД_Страница2", Тип("ГруппаФормы"), Элементы.МОД_СтраницыФормы); НоваяСтраница.Вид = ВидГруппыФормы.Страница; НоваяСтраница.Заголовок = "Страница 2"; // 4. Разместим на страницах что нибудь, чтобы они в принципе отображались. // пустые страницы на форме отображаться не будут НоваяДекорация = Элементы.Добавить("МОД_ДекарацияСтраница1", Тип("ДекорацияФормы"), Элементы.МОД_Страница1); НоваяДекорация.Заголовок = "Декарация страница 1"; НоваяДекорация = Элементы.Добавить("МОД_ДекарацияСтраница2", Тип("ДекорацияФормы"), Элементы.МОД_Страница2); НоваяДекорация.Заголовок = "Декарация страница 2"; |

Перемещение элементов формы
А давайте теперь контрагента из группы с табличной значений переместим на страницу 2. И поставим его перед декорацией.
1 |
Элементы.Переместить(Элементы.МОД_Контрагент, Элементы.МОД_Страница2, Элементы.МОД_ДекарацияСтраница2); |

Динамические списки
Про динамические списки у меня есть отдельная статья, посвященная только им.
Лайфхаки
Для некоторого упрощения можно использовать использовать библиотеки для упрощенной генерации элементов форм. Первая попавшаяся: https://infostart.ru/1c/articles/1270443/
Интересный инструмент – “Декомпилятор управляемых форм” https://infostart.ru/public/304736/ Поддерживает не все свойства элементов форм. Да и сгенерированный код получается немного “грязноватым”, но бывает очень удобно накидать в конфигураторе элементов и в режиме предприятия просто сгенерировать значительную часть кода.
Файлы и заключение
Когда соберетесь модифицировать формы, то не пишите весь код в событии ПриСозданииНаСервере, а выносите блоками. Блок с табличной частью, блок с какой то одной группой итп. Я к такому привык очень быстро, когда при разработке приложений под ОС Android пересел с View на JetPack Compose. В котором вообще весь интерфейс генерируется кодом.
Подготовленный пример в виде внешней обработки, который работает на БП, РТ, УНФ, УТ, КА2, ERP