Программное создание элементов формы 1С сводится к трём шагам: добавить реквизиты методом ИзменитьРеквизиты, создать команды через Команды.Добавить и вывести всё на форму через Элементы.Добавить с нужным типом — ПолеФормы, ГруппаФормы, КнопкаФормы, ДекорацияФормы. Делается это на сервере, обычно в ПриСозданииНаСервере. Практически любое свойство, которое задаётся в палитре свойств конфигуратора, доступно и из кода.

Зачем так делать: доработка типовой формы кодом почти не конфликтует с обновлениями. Визуально перенесённые элементы при каждом обновлении приходится сравнивать и объединять, а код, который создаёт свои элементы, продолжает работать. Ниже — все основные операции с примерами и ловушки, на которые я натыкался.

Порядок работы и где писать код

Программное создание элементов формы 1С: реквизиты, команды и элементы добавляются кодом при создании формы
Программное создание элементов формы 1С: реквизиты, команды и элементы добавляются кодом при создании формы

Правильная последовательность:

  1. Добавить реквизиты (ИзменитьРеквизиты) — без реквизита не будет данных для поля.
  2. Добавить команды (Команды.Добавить).
  3. Создать группы, затем поля, кнопки и декорации внутри групп.
  4. Назначить обработчики событий (УстановитьДействие).

Код пишется в процедуре, вызываемой из ПриСозданииНаСервере. Если доработка делается в расширении, удобно подключиться к этому событию аннотацией &После и вызвать свою процедуру — тогда типовой модуль формы не меняется совсем. Всем своим объектам давайте префикс (пр_ в примерах), чтобы не пересечься с именами, которые появятся в типовой форме после обновления.

Добавление реквизитов формы

Реквизит описывается объектом РеквизитФормы: имя, описание типов, путь (пустая строка — реквизит верхнего уровня) и заголовок.

1С
&НаСервере
Процедура пр_ДобавитьРеквизиты()

	Реквизиты = Новый Массив;
	Реквизиты.Добавить(Новый РеквизитФормы("пр_Использовать", Новый ОписаниеТипов("Булево"), "", "Использовать"));
	Реквизиты.Добавить(Новый РеквизитФормы("пр_Номенклатура",
		Новый ОписаниеТипов("СправочникСсылка.Номенклатура"), "", "Номенклатура"));
	Реквизиты.Добавить(Новый РеквизитФормы("пр_Характеристика",
		Новый ОписаниеТипов("СправочникСсылка.ХарактеристикиНоменклатуры"), "", "Характеристика"));
	Реквизиты.Добавить(Новый РеквизитФормы("пр_Количество",
		Новый ОписаниеТипов("Число", , , Новый КвалификаторыЧисла(15, 3)), "", "Количество"));
	Реквизиты.Добавить(Новый РеквизитФормы("пр_Комментарий",
		Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)), "", "Комментарий"));

	ИзменитьРеквизиты(Реквизиты);

КонецПроцедуры

Важные моменты:

  • ИзменитьРеквизиты перестраивает данные формы. Вызывайте его один раз со всеми реквизитами, а не в цикле по одному.
  • Удалять можно только программно добавленные реквизиты — вторым параметром, массивом путей: ИзменитьРеквизиты(, УдаляемыеРеквизиты).
  • Чтобы добавить колонку в табличную часть на форме, укажите путь: Новый РеквизитФормы("пр_Скидка", ОписаниеЧисла, "Объект.Товары"). Колонка будет только у данных формы, в объект она не запишется.
  • В конфигурациях на БСП описания типов короче через ОбщегоНазначения.ОписаниеТипаСтрока(100), ОписаниеТипаЧисло(15, 3) и ОписаниеТипаДата(ЧастиДаты.Дата).

Обращаться к добавленному реквизиту в коде можно только через квадратные скобки: ЭтотОбъект["пр_Количество"]. Запись пр_Количество = 5 не скомпилируется — в метаданных формы такого реквизита нет.

Группы и страницы

1С
&НаСервере
Процедура пр_СоздатьГруппы()

	Страницы = Элементы.Добавить("пр_Страницы", Тип("ГруппаФормы"), ЭтотОбъект);
	Страницы.Вид = ВидГруппыФормы.Страницы;

	Страница = Элементы.Добавить("пр_СтраницаЗаказ", Тип("ГруппаФормы"), Страницы);
	Страница.Вид = ВидГруппыФормы.Страница;
	Страница.Заголовок = "Дозаказ";

	Группа = Элементы.Добавить("пр_ГруппаПоля", Тип("ГруппаФормы"), Страница);
	Группа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
	Группа.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная;
	Группа.ОтображатьЗаголовок = Ложь;

КонецПроцедуры

Виды групп: обычная группа, страницы, страница, командная панель, группа колонок (для таблиц), контекстное меню, подменю, группа кнопок. Третий параметр Элементы.Добавить — родитель; если элемент нужно поставить в определённое место среди уже существующих, используйте Элементы.Вставить(Имя, Тип, Родитель, ПередЭлементом). Чтобы переместить существующий элемент — Элементы.Переместить(Элемент, НовыйРодитель, ПередЭлементом).

Поля формы: ввод, флажок, параметры и связи выбора

1С
&НаСервере
Процедура пр_СоздатьПоля()

	Родитель = Элементы.пр_ГруппаПоля;

	Флажок = Элементы.Добавить("пр_Использовать", Тип("ПолеФормы"), Родитель);
	Флажок.ПутьКДанным = "пр_Использовать";
	Флажок.Вид = ВидПоляФормы.ПолеФлажка;
	Флажок.ПоложениеЗаголовка = ПоложениеЗаголовкаЭлементаФормы.Право;
	Флажок.УстановитьДействие("ПриИзменении", "пр_ИспользоватьПриИзменении");

	ПолеНоменклатуры = Элементы.Добавить("пр_Номенклатура", Тип("ПолеФормы"), Родитель);
	ПолеНоменклатуры.ПутьКДанным = "пр_Номенклатура";
	ПолеНоменклатуры.Вид = ВидПоляФормы.ПолеВвода;
	// Не показывать помеченные на удаление
	ПараметрыВыбора = Новый Массив;
	ПараметрыВыбора.Добавить(Новый ПараметрВыбора("Отбор.ПометкаУдаления", Ложь));
	ПолеНоменклатуры.ПараметрыВыбора = Новый ФиксированныйМассив(ПараметрыВыбора);

	ПолеХарактеристики = Элементы.Добавить("пр_Характеристика", Тип("ПолеФормы"), Родитель);
	ПолеХарактеристики.ПутьКДанным = "пр_Характеристика";
	ПолеХарактеристики.Вид = ВидПоляФормы.ПолеВвода;
	// Характеристики только выбранной номенклатуры
	СвязиВыбора = Новый Массив;
	СвязиВыбора.Добавить(Новый СвязьПараметраВыбора("Отбор.Владелец", "пр_Номенклатура"));
	ПолеХарактеристики.СвязиПараметровВыбора = Новый ФиксированныйМассив(СвязиВыбора);

	ПолеКоличества = Элементы.Добавить("пр_Количество", Тип("ПолеФормы"), Родитель);
	ПолеКоличества.ПутьКДанным = "пр_Количество";
	ПолеКоличества.Вид = ВидПоляФормы.ПолеВвода;
	ПолеКоличества.ФорматРедактирования = "ЧДЦ=3; ЧН=0";

	ПолеКомментария = Элементы.Добавить("пр_Комментарий", Тип("ПолеФормы"), Родитель);
	ПолеКомментария.ПутьКДанным = "пр_Комментарий";
	ПолеКомментария.Вид = ВидПоляФормы.ПолеВвода;
	ПолеКомментария.РежимВыбораИзСписка = Истина;
	ПолеКомментария.СписокВыбора.Добавить("Необходимо дозаказать");
	ПолеКомментария.СписокВыбора.Добавить("Достаточно");

КонецПроцедуры

ПараметрыВыбора и СвязиПараметровВыбора — фиксированные массивы: изменить их «на месте» нельзя, всегда собирайте новый массив и присваивайте целиком. Если у поля уже были параметры из конфигуратора и их нужно сохранить, сначала скопируйте существующие элементы в свой массив.

Команды и кнопки

1С
&НаСервере
Процедура пр_СоздатьКоманды()

	Команда = Команды.Добавить("пр_ЗаполнитьДозаказ");
	Команда.Действие = "пр_ЗаполнитьДозаказ";
	Команда.Заголовок = "Заполнить дозаказ";
	Команда.Картинка = БиблиотекаКартинок.ЗаполнитьФорму;
	Команда.Отображение = ОтображениеКнопки.КартинкаИТекст;

	Кнопка = Элементы.Добавить("пр_ЗаполнитьДозаказ", Тип("КнопкаФормы"), Элементы.ФормаКоманднаяПанель);
	Кнопка.ИмяКоманды = "пр_ЗаполнитьДозаказ";

КонецПроцедуры

&НаКлиенте
Процедура пр_ЗаполнитьДозаказ(Команда)
	// Обработчик команды: процедура с этим именем должна быть в модуле формы
КонецПроцедуры

Обработчик команды — процедура в модуле формы (или в модуле формы расширения). Картинку выбирайте из тех, что реально есть в вашей конфигурации: имена в библиотеке картинок различаются. Удаляются, как и реквизиты, только программно созданные команды: Команды.Удалить(Команда).

Декорации

1С
Надпись = Элементы.Добавить("пр_Подсказка", Тип("ДекорацияФормы"), Элементы.пр_ГруппаПоля);
Надпись.Вид = ВидДекорацииФормы.Надпись;
Надпись.Заголовок = "Количество рассчитывается по остаткам на складе";
Надпись.ЦветТекста = ЦветаСтиля.ПоясняющийТекст;

Для картинки — ВидДекорацииФормы.Картинка и свойство Картинка. Цвет стиля из примера есть в большинстве типовых конфигураций на БСП, но проверьте, что он объявлен у вас.

Обработчики событий через УстановитьДействие

У элемента и у самой формы есть метод УстановитьДействие(ИмяСобытия, ИмяПроцедуры). Для своих элементов это единственный способ назначить обработчик. Для существующих элементов типовой формы им можно подменить обработчик — но тогда типовой обработчик перестанет вызываться, и его надо вызвать из своего вручную:

1С
// В ПриСозданииНаСервере
Элементы.Номенклатура.УстановитьДействие("ПриИзменении", "пр_НоменклатураПриИзменении");

&НаКлиенте
Процедура пр_НоменклатураПриИзменении(Элемент)

	НоменклатураПриИзменении(Элемент); // типовой обработчик
	пр_ОбновитьДозаказ();

КонецПроцедуры

В расширениях этот приём почти не нужен: обработчик типовой формы дополняется аннотацией &После. Подмену через УстановитьДействие я оставляю для случаев, когда у элемента вообще не было обработчика события.

Типичные ошибки

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

Доработки типовых форм таким способом делаю в рамках доработки 1С и разработки расширений — это заметно удешевляет последующее обновление 1С.

Итог

  • Реквизиты добавляются одним вызовом ИзменитьРеквизиты с массивом РеквизитФормы, удаляются — только программно созданные.
  • Элементы создаются через Элементы.Добавить или Элементы.Вставить; сначала группы, потом поля и кнопки.
  • Команда — это Команды.Добавить плюс кнопка с ИмяКоманды и процедура-обработчик в модуле формы.
  • Параметры и связи выбора задаются фиксированными массивами целиком.
  • Обработчики своих элементов назначаются через УстановитьДействие; в расширениях для типовых обработчиков используйте &После.