Программное изменение формы 1С в расширении — это добавление полей, групп, страниц и команд кодом в обработчике ПриСозданииНаСервере заимствованной формы (с аннотацией &После), вместо перетаскивания элементов в редакторе формы расширения. Используются методы Элементы.Добавить / Элементы.Вставить, ИзменитьРеквизиты, Команды.Добавить и УстановитьДействие. Такой подход заметно проще переживает обновления типовой конфигурации: код добавляет элементы поверх любой версии формы.
Ниже разберу, чем плох путь через редактор, соберу небольшой общий модуль для создания элементов и по шагам доработаю форму контрагента: поле нового реквизита, флажок видимости, группа, отдельная страница и команда.
Редактор формы в расширении или код
Классический путь: заимствовать справочник и форму в расширение, добавить реквизит объекту, перетащить его на форму, создать группу, страницу, команду. Это быстро, но у способа есть цена.
| Критерий | Редактор формы расширения | Программное изменение |
|---|---|---|
| Скорость первой доработки | Быстрее | Медленнее |
| Видно ли элементы в конфигураторе | Да | Нет, только в режиме «Предприятие» |
| Поведение при обновлении типовой формы | Нужно сравнение, возможны конфликты и «поехавшая» форма | Обычно работает без изменений |
| Зависимость от структуры типовой формы | Полная | Только от имён родительских групп |
| Перенос между конфигурациями | Сложно | Общий модуль переносится как есть |

На проектах я делаю так: если доработка формы — пара полей в типовой конфигурации, которая регулярно обновляется, — только кодом. Редактор формы расширения оставляю для собственных форм и для случаев, где программно получится нечитаемо (сложные таблицы, много условного оформления).
Подготовка расширения
- Создайте расширение, задайте префикс, например
Расш_. - Заимствуйте справочник
Контрагентыи добавьте ему реквизитРасш_КонтактноеЛицо(строка). Для расширения, меняющего структуру данных, база выполнит реструктуризацию при применении; проверьте, что в свойствах расширения это допускается. - Заимствуйте форму элемента справочника. Элементы в редакторе не трогаем.
- В модуле формы расширения создайте обработчик:
&НаСервере
&После("ПриСозданииНаСервере")
Процедура Расш_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
Расш_ДобавитьЭлементыКонтактногоЛица();
КонецПроцедурыОбщий модуль для создания элементов
Чтобы обработчик формы читался как описание, а не как стена кода, выношу создание элементов в серверный общий модуль расширения Расш_ЭлементыФормы.
#Область ПрограммныйИнтерфейс
// Создаёт элемент формы. Если указан ЭлементПеред — вставляет перед ним.
Функция СоздатьЭлемент(Форма, Имя, ТипЭлемента, Родитель = Неопределено, ЭлементПеред = Неопределено) Экспорт
Если Родитель = Неопределено Тогда
Родитель = Форма;
КонецЕсли;
Если ЭлементПеред = Неопределено Тогда
Возврат Форма.Элементы.Добавить(Имя, ТипЭлемента, Родитель);
КонецЕсли;
Возврат Форма.Элементы.Вставить(Имя, ТипЭлемента, Родитель, ЭлементПеред);
КонецФункции
// Устанавливает свойства элемента из структуры «ИмяСвойства — Значение».
Процедура УстановитьСвойства(Элемент, Свойства) Экспорт
Если Свойства = Неопределено Тогда
Возврат;
КонецЕсли;
ЗаполнитьЗначенияСвойств(Элемент, Свойства);
КонецПроцедуры
// Связывает события элемента с процедурами модуля формы:
// структура «ИмяСобытия — ИмяПроцедуры».
Процедура УстановитьСобытия(Элемент, События) Экспорт
Если События = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого Событие Из События Цикл
Элемент.УстановитьДействие(Событие.Ключ, Событие.Значение);
КонецЦикла;
КонецПроцедуры
// Создаёт поле формы, связанное с данными.
Функция СоздатьПоле(Форма, Имя, ПутьКДанным, Родитель = Неопределено, Вид = Неопределено,
ЭлементПеред = Неопределено, Свойства = Неопределено, События = Неопределено) Экспорт
Поле = СоздатьЭлемент(Форма, Имя, Тип("ПолеФормы"), Родитель, ЭлементПеред);
Поле.Вид = ?(Вид = Неопределено, ВидПоляФормы.ПолеВвода, Вид);
Поле.ПутьКДанным = ПутьКДанным;
УстановитьСвойства(Поле, Свойства);
УстановитьСобытия(Поле, События);
Возврат Поле;
КонецФункции
// Добавляет реквизит формы указанного типа.
Процедура СоздатьРеквизит(Форма, Имя, ОписаниеТипов, Заголовок = "") Экспорт
Добавляемые = Новый Массив;
Добавляемые.Добавить(Новый РеквизитФормы(Имя, ОписаниеТипов, , Заголовок));
Форма.ИзменитьРеквизиты(Добавляемые);
КонецПроцедуры
// Создаёт обычную группу; по умолчанию — горизонтальную без рамки.
Функция СоздатьГруппу(Форма, Имя, Родитель = Неопределено, ЭлементПеред = Неопределено,
Свойства = Неопределено) Экспорт
Группа = СоздатьЭлемент(Форма, Имя, Тип("ГруппаФормы"), Родитель, ЭлементПеред);
Группа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
Группа.Группировка = ГруппировкаПодчиненныхЭлементовФормы.ГоризонтальнаяВсегда;
Группа.Отображение = ОтображениеОбычнойГруппы.Нет;
Группа.ОтображатьЗаголовок = Ложь;
УстановитьСвойства(Группа, Свойства);
Возврат Группа;
КонецФункции
// Создаёт страницу в группе страниц.
Функция СоздатьСтраницу(Форма, Имя, ГруппаСтраниц, Заголовок, СтраницаПеред = Неопределено) Экспорт
Страница = СоздатьЭлемент(Форма, Имя, Тип("ГруппаФормы"), ГруппаСтраниц, СтраницаПеред);
Страница.Вид = ВидГруппыФормы.Страница;
Страница.Заголовок = Заголовок;
Страница.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная;
Возврат Страница;
КонецФункции
// Создаёт команду формы и кнопку для неё.
Функция СоздатьКомандуИКнопку(Форма, ИмяКоманды, Действие, Заголовок, Родитель = Неопределено) Экспорт
Команда = Форма.Команды.Добавить(ИмяКоманды);
Команда.Действие = Действие;
Команда.Заголовок = Заголовок;
Кнопка = СоздатьЭлемент(Форма, "Кнопка" + ИмяКоманды, Тип("КнопкаФормы"), Родитель);
Кнопка.ИмяКоманды = ИмяКоманды;
Возврат Кнопка;
КонецФункции
#КонецОбластиОбратите внимание на типы: на управляемой форме поле — это ПолеФормы с нужным Вид (ПолеВвода, ПолеФлажка, ПолеНадписи и т. д.), а не ПолеВвода, как в обычных формах.
Собираем доработку формы
Задача: на форме контрагента нужна новая страница «Контакты», на ней в одну строку — флажок «Показывать контактное лицо» и поле реквизита Расш_КонтактноеЛицо, ниже — кнопка, которая сообщает контактное лицо.

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




