Добавить реквизит формы программно приходится, когда типовую конфигурацию не хочется снимать с поддержки, набор полей зависит от настроек или нужно встроиться в чужую форму из расширения. Ниже — рабочий пример для управляемой формы 8.3 и разбор каждого шага: реквизит и поле для него, команда с кнопкой, группа и декорации, обработчики событий и перенос всего этого в расширение.
Если конфигурация снята с поддержки, элементы проще нарисовать в редакторе формы. Код оправдан в трёх случаях:
РеквизитФормы и один раз вызвать ИзменитьРеквизиты.Элементы.Добавить: группы, поля, декорации. Поле связать с реквизитом через ПутьКДанным.Команды.Добавить, указать Действие, затем создать кнопки со свойством ИмяКоманды.УстановитьДействие и написать их в модуле формы.Структура формы меняется только на сервере, обычно в обработчике ПриСозданииНаСервере. ИзменитьРеквизиты, Элементы.Добавить, Команды.Добавить и УстановитьДействие доступны только в серверном контексте формы. На клиенте их нет — ни в тонком, ни в толстом, ни в веб- и мобильном. Поэтому из ПриОткрытии форму не достраивают, а в процедуре &НаСервереБезКонтекста это не выйдет: там нет формы. На клиенте меняют только свойства готовых элементов: видимость, доступность, заголовок.
Код добавляет в конец формы группу «Дополнительно»: строку подсказки из картинки и надписи-гиперссылки, многострочное поле «Комментарий менеджера», флажок «Срочно» и команду «Очистить комментарий» — кнопкой в группе и в командной панели. Префикс Доп_ стоит во всех именах, от реквизитов до процедур: так они не совпадут с тем, что уже есть на форме.
Обработчики управляемой формы назначаются в палитре свойств, а не по имени процедуры, поэтому ПриСозданииНаСервере из листинга не вставляйте текстом: как быть, если обработчик уже есть, сказано в комментарии в начале кода. Для картинки БиблиотекаКартинок.Информация нужна платформа 8.3.17 или новее, на старых версиях подставьте общую картинку конфигурации.
// Модуль управляемой формы. Платформа 1С:Предприятие 8.3.17 и новее
// (СтрНайти — с 8.3.6, БиблиотекаКартинок.Информация — с 8.3.17).
// Программно добавляем: реквизиты, группы, декорации (картинка и надпись),
// поля ввода, команду и две кнопки, обработчики событий элементов.
// В расширении замените Доп_ на префикс расширения (например, Расш1_) во ВСЕХ именах:
// реквизитов, элементов, команды и процедур, включая обработчики (Расш1_НадписьНажатие и т. п.).
// Если у формы уже есть обработчик ПриСозданииНаСервере, второй не создавайте:
// добавьте в конец существующего вызов Доп_ДобавитьЭлементыПрограммно().
// Если обработчика нет, создайте его через палитру свойств формы,
// иначе процедура, вставленная текстом, вызываться не будет.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// ... существующий код формы ...
Доп_ДобавитьЭлементыПрограммно();
КонецПроцедуры
&НаСервере
Процедура Доп_ДобавитьЭлементыПрограммно()
// Защита от повторного вызова: если группа уже есть, форма уже достроена
Если Элементы.Найти("Доп_Группа") <> Неопределено Тогда
Возврат;
КонецЕсли;
// 1. Реквизиты формы: собираем в массив и добавляем одним вызовом
ДобавляемыеРеквизиты = Новый Массив;
ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы("Доп_Комментарий",
Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(300)), , "Комментарий менеджера"));
ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы("Доп_Срочно",
Новый ОписаниеТипов("Булево"), , "Срочно"));
ИзменитьРеквизиты(ДобавляемыеРеквизиты);
// К новым реквизитам нельзя обращаться просто по имени, только через ЭтотОбъект:
// ЭтотОбъект.Доп_Комментарий или индексатор ЭтотОбъект["Доп_Комментарий"]
ЭтотОбъект["Доп_Комментарий"] = "";
ЭтотОбъект["Доп_Срочно"] = Ложь;
// 2. Группа-контейнер в конце формы
Группа = Элементы.Добавить("Доп_Группа", Тип("ГруппаФормы"), ЭтотОбъект);
Группа.Вид = ВидГруппыФормы.ОбычнаяГруппа;
Группа.Заголовок = "Дополнительно";
Группа.ОтображатьЗаголовок = Истина;
Группа.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная;
// 3. Строка подсказки: картинка и надпись-гиперссылка рядом
ГруппаПодсказки = Элементы.Добавить("Доп_ГруппаПодсказки", Тип("ГруппаФормы"), Группа);
ГруппаПодсказки.Вид = ВидГруппыФормы.ОбычнаяГруппа;
ГруппаПодсказки.ОтображатьЗаголовок = Ложь;
ГруппаПодсказки.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Горизонтальная;
ДекорацияКартинка = Элементы.Добавить("Доп_Картинка", Тип("ДекорацияФормы"), ГруппаПодсказки);
ДекорацияКартинка.Вид = ВидДекорацииФормы.Картинка;
// БиблиотекаКартинок.Информация есть в платформе с 8.3.17.
// На более старых версиях укажите общую картинку своей конфигурации:
// ДекорацияКартинка.Картинка = БиблиотекаКартинок.<ИмяОбщейКартинки>;
ДекорацияКартинка.Картинка = БиблиотекаКартинок.Информация;
ДекорацияНадпись = Элементы.Добавить("Доп_Надпись", Тип("ДекорацияФормы"), ГруппаПодсказки);
ДекорацияНадпись.Вид = ВидДекорацииФормы.Надпись;
ДекорацияНадпись.Заголовок = "Как заполнять эти поля?";
ДекорацияНадпись.Гиперссылка = Истина;
ДекорацияНадпись.УстановитьДействие("Нажатие", "Доп_НадписьНажатие");
// 4. Поля, привязанные к новым реквизитам
ПолеКомментарий = Элементы.Добавить("Доп_ПолеКомментарий", Тип("ПолеФормы"), Группа);
ПолеКомментарий.Вид = ВидПоляФормы.ПолеВвода;
ПолеКомментарий.ПутьКДанным = "Доп_Комментарий";
ПолеКомментарий.МногострочныйРежим = Истина;
ПолеКомментарий.Высота = 2;
ПолеКомментарий.УстановитьДействие("ПриИзменении", "Доп_КомментарийПриИзменении");
ПолеСрочно = Элементы.Добавить("Доп_ПолеСрочно", Тип("ПолеФормы"), Группа);
ПолеСрочно.Вид = ВидПоляФормы.ПолеФлажка;
ПолеСрочно.ПутьКДанным = "Доп_Срочно";
// 5. Команда формы и кнопки для неё
КомандаОчистить = Команды.Добавить("Доп_ОчиститьКомментарий");
КомандаОчистить.Заголовок = "Очистить комментарий";
КомандаОчистить.Подсказка = "Очистить поле ""Комментарий менеджера"" и снять флажок ""Срочно""";
КомандаОчистить.Действие = "Доп_ОчиститьКомментарий";
// Обычная кнопка внутри группы
Кнопка = Элементы.Добавить("Доп_КнопкаОчистить", Тип("КнопкаФормы"), Группа);
Кнопка.Вид = ВидКнопкиФормы.ОбычнаяКнопка;
Кнопка.ИмяКоманды = КомандаОчистить.Имя;
// Та же команда в командной панели формы
КнопкаПанели = Элементы.Добавить("Доп_КнопкаОчиститьПанель", Тип("КнопкаФормы"), КоманднаяПанель);
КнопкаПанели.Вид = ВидКнопкиФормы.КнопкаКоманднойПанели;
КнопкаПанели.ИмяКоманды = КомандаОчистить.Имя;
КонецПроцедуры
&НаКлиенте
Процедура Доп_НадписьНажатие(Элемент)
ПоказатьПредупреждение(, "Опишите, на что обратить внимание. "
+ "Если в тексте есть слово ""срочно"", флажок ""Срочно"" поставится сам.");
КонецПроцедуры
&НаКлиенте
Процедура Доп_КомментарийПриИзменении(Элемент)
// СтрНайти доступна с платформы 8.3.6
Если СтрНайти(НРег(ЭтотОбъект["Доп_Комментарий"]), "срочно") > 0 Тогда
ЭтотОбъект["Доп_Срочно"] = Истина;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура Доп_ОчиститьКомментарий(Команда)
ЭтотОбъект["Доп_Комментарий"] = "";
ЭтотОбъект["Доп_Срочно"] = Ложь;
КонецПроцедуры
Чтобы создать реквизит формы программно, вызовите метод формы ИзменитьРеквизиты(ДобавляемыеРеквизиты, УдаляемыеРеквизиты). Он принимает два массива: объекты РеквизитФормы для добавления и строки с именами реквизитов для удаления. Любой параметр можно опустить.
Конструктор Новый РеквизитФормы(Имя, ТипЗначения, Путь, Заголовок, СохраняемыеДанные):
| Параметр | Что передать |
|---|---|
| Имя | Уникальное в пределах родителя, не начинается с цифры. |
| ТипЗначения | ОписаниеТипов — один или несколько типов с квалификаторами. |
| Путь | Путь к родительскому реквизиту. Пусто — реквизит верхнего уровня, "ТаблицаДанных" — колонка реквизита-таблицы, "Объект.Товары" — колонка табличной части в данных формы. |
| Заголовок | Строка. Её покажет поле, если у элемента не задан собственный заголовок. |
| СохраняемыеДанные | Истина — изменение значения помечает форму как изменённую, и при закрытии пользователь получит вопрос о сохранении. По умолчанию Ложь. |
Типы описываются через ОписаниеТипов. Частые варианты:
// Строка до 150 символов
ТипСтрока = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150));
// Число 15.2, только неотрицательное
ТипСумма = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Неотрицательный));
// Дата без времени
ТипДата = Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата));
// Ссылка на справочник (имя справочника подставьте своё)
ТипКонтрагент = Новый ОписаниеТипов("СправочникСсылка.Контрагенты");
// Таблица значений для реквизита-таблицы
ТипТаблица = Новый ОписаниеТипов("ТаблицаЗначений");
После ИзменитьРеквизиты к новому реквизиту нельзя обратиться просто по имени: при компиляции модуля его ещё нет, и конфигуратор ответит «Переменная не определена (Доп_Комментарий)». Обращайтесь через ЭтотОбъект: ЭтотОбъект.Доп_Комментарий (так рекомендует синтакс-помощник) или индексатором ЭтотОбъект["Доп_Комментарий"], если имя реквизита собирается в строке. Оба варианта работают и на сервере, и на клиенте.
Элементы.Добавить(Имя, Тип, Родитель) создаёт элемент и возвращает его. Тип передаётся как Тип("ПолеФормы"), Тип("ГруппаФормы"), Тип("КнопкаФормы"), Тип("ДекорацияФормы") или Тип("ТаблицаФормы"). Родитель — группа, таблица или сама форма (ЭтотОбъект); без него элемент попадёт в конец формы.
У поля задайте Вид и ПутьКДанным. Для строки, числа, даты и ссылки подходит ВидПоляФормы.ПолеВвода, для булева — ВидПоляФормы.ПолеФлажка, для текста только на чтение — ВидПоляФормы.ПолеНадписи. Остальное по ситуации: МногострочныйРежим, Высота, ТолькоПросмотр, ПоложениеЗаголовка, Подсказка.
Команды формы добавляют методом Команды.Добавить(Имя), он возвращает объект КомандаФормы. Главное свойство — Действие: строка с именем процедуры модуля формы, которая выполнится при нажатии. Дополнительно задают Заголовок, Подсказка, Картинка. Как устроена команда и как добавить её в конфигураторе, разобрано в статье «Команды формы 1С», здесь — только программный способ.
Без кнопки команда на форме не видна. Кнопка — это элемент Тип("КнопкаФормы") со свойством ИмяКоманды. Вид кнопки зависит от места: ВидКнопкиФормы.ОбычнаяКнопка — внутри группы, ВидКнопкиФормы.КнопкаКоманднойПанели — в командной панели (родитель — свойство формы КоманднаяПанель), ВидКнопкиФормы.Гиперссылка — кнопка-ссылка. Одну команду можно вывести несколькими кнопками.
Процедура команды объявляется с &НаКлиенте и принимает один параметр — саму команду. Если внутри нужен запрос к базе, вызовите из неё серверную процедуру.
Группа Тип("ГруппаФормы") с Вид = ВидГруппыФормы.ОбычнаяГруппа — контейнер для раскладки. Свойство Группировка задаёт направление: ГруппировкаПодчиненныхЭлементовФормы.Вертикальная или Горизонтальная, ОтображатьЗаголовок включает заголовок группы. Для закладок есть виды Страницы и Страница, для своей панели кнопок — КоманднаяПанель.
Декорация Тип("ДекорацияФормы") с данными не связана. ВидДекорацииФормы.Надпись выводит текст из свойства Заголовок, а с Гиперссылка = Истина становится ссылкой с событием Нажатие. ВидДекорацииФормы.Картинка показывает картинку из библиотеки платформы, общую картинку конфигурации или собранную из двоичных данных.
Созданные кодом элементы приходят без обработчиков. Метод элемента УстановитьДействие(ИмяСобытия, ИмяПроцедуры) связывает событие с процедурой модуля формы: "ПриИзменении" у поля, "Нажатие" у декорации, "Выбор" у таблицы. Процедура должна существовать в модуле формы, иметь ту же директиву компиляции и те же параметры, что сгенерировал бы для этого события конфигуратор. Для большинства событий это &НаКлиенте, для серверных, например ПриПолученииДанныхНаСервере у таблицы динамического списка, — &НаСервереБезКонтекста.
По стандарту разработки 1С № 492 такие процедуры называют с префиксом Подключаемый_, чтобы при поиске неиспользуемых процедур их не приняли за мёртвый код. Если код проверяют по стандартам, ставьте Подключаемый_ после своего префикса: Доп_Подключаемый_НадписьНажатие.
Элементы.Добавить ставит элемент в конец родителя. Вставить перед конкретным элементом можно методом Элементы.Вставить(Имя, Тип, Родитель, МестоРасположения), перенести готовый — Элементы.Переместить(Элемент, Родитель, МестоРасположения). Соседа в типовой форме ищите через Элементы.Найти: если после обновления его переименуют, метод вернёт Неопределено, и код пропустит перенос, а не упадёт. Проверьте и родителя соседа: обычную группу нельзя поставить в таблицу, группу колонок, командную панель или подменю.
// Фрагмент для конца Доп_ДобавитьЭлементыПрограммно (контекст &НаСервере)
Сосед = Элементы.Найти("Комментарий");
ДопГруппа = Элементы.Найти("Доп_Группа");
Если Сосед <> Неопределено И ДопГруппа <> Неопределено Тогда
РодительСоседа = Сосед.Родитель;
// Обычную группу можно поставить только в форму, обычную группу или страницу.
// И/Или вычисляются сокращённо: свойство Вид читаем, только если это ГруппаФормы.
МожноПереместить = ТипЗнч(РодительСоседа) <> Тип("ТаблицаФормы")
И (ТипЗнч(РодительСоседа) <> Тип("ГруппаФормы")
Или РодительСоседа.Вид = ВидГруппыФормы.ОбычнаяГруппа
Или РодительСоседа.Вид = ВидГруппыФормы.Страница);
Если МожноПереместить Тогда
Элементы.Переместить(ДопГруппа, РодительСоседа, Сосед);
КонецЕсли;
КонецЕсли;
Бывает нужно понять, лежит ли элемент внутри определённой группы. Поднимаемся по свойству Родитель, пока не дойдём до формы:
&НаКлиентеНаСервереБезКонтекста
Функция ЭлементВнутриГруппы(Элемент, Группа)
Текущий = Элемент.Родитель;
Пока ТипЗнч(Текущий) = Тип("ГруппаФормы") Или ТипЗнч(Текущий) = Тип("ТаблицаФормы") Цикл
// Имена элементов на форме уникальны, поэтому сравниваем по имени
Если Текущий.Имя = Группа.Имя Тогда
Возврат Истина;
КонецЕсли;
Текущий = Текущий.Родитель;
КонецЦикла;
Возврат Ложь;
КонецФункции
Механика та же, меняется точка входа. Подробнее о заимствовании форм — в статье «Расширение формы 1С».
ПриСозданииНаСервере и создайте обработчик с порядком вызова «После». Конфигуратор создаст процедуру вида Расш1_ПриСозданииНаСервереПосле.&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
Если Отказ Тогда
Возврат;
КонецЕсли;
Расш1_ДобавитьЭлементыПрограммно();
КонецПроцедуры
О чём помнить в расширении:
Расш1_), иначе совпадение с элементом из нового релиза типовой сломает открытие формы. В полном примере достаточно заменить Доп_ на Расш1_, а его ПриСозданииНаСервере заменит Расш1_ПриСозданииНаСервереПосле.Действие и УстановитьДействие пишите в модуле заимствованной формы в расширении.Отказ в начале обработчика: если основной код уже отказался открывать форму, достраивать её бессмысленно.&После("ПриСозданииНаСервере"). Она перехватывает процедуру модуля по имени, а не событие: если такой процедуры в исходной форме нет или её переименуют в новом релизе, расширение перестанет применяться. Для событий формы надёжнее обработчик «После» из палитры свойств.ЭтотОбъект.Доп_Комментарий или ЭтотОбъект["Доп_Комментарий"]. Коварнее присваивание Доп_Комментарий = "": оно компилируется, но создаёт локальную переменную, а реквизит формы не меняется.ПриСозданииНаСервере уже есть. Оставьте одну процедуру и перенесите в неё вызов.ПриСозданииНаСервере и повторяется по команде «Перечитать», так что второй вызов наткнётся на занятые имена. Элементы создавайте в ПриСозданииНаСервере, а в ПриЧтенииНаСервере только загружайте значения.ПутьКДанным. Элемент создали раньше реквизита, опечатались в имени или забыли вызвать ИзменитьРеквизиты. Порядок всегда один: сначала реквизиты, потом элементы.Действие не совпадает с именем процедуры, процедура объявлена &НаСервере или без параметра. Строку с именем синтаксический контроль не проверяет, поэтому ошибку увидите только при выполнении, а не при сохранении модуля.ИзменитьРеквизиты удаляются только реквизиты, добавленные программно. Реквизит из конфигуратора остаётся на форме, а связанное с ним поле прячут свойством Видимость.Создайте реквизит-таблицу Новый РеквизитФормы("ТаблицаДанных", Новый ОписаниеТипов("ТаблицаЗначений")) и её колонки с путём "ТаблицаДанных" в третьем параметре — одним вызовом ИзменитьРеквизиты. На форму добавьте элемент Тип("ТаблицаФормы") с ПутьКДанным = "ТаблицаДанных", а в него — поля с путями вида "ТаблицаДанных.Цена". Заполнить таблицу удобно через ЗначениеВРеквизитФормы(ТЗ, "ТаблицаДанных").
В табличную часть объекта колонку тоже можно добавить, но только в данные формы: в базу она не пишется и сбрасывается, когда форма заново получает объект, например после записи или «Перечитать». Чтобы значение сохранялось, в расширении добавляют реквизит в заимствованную табличную часть (платформа 8.3.11 и новее).
// Колонка только в данных формы: в базу она не записывается.
// Элементы.Товары — таблица формы, связанная с Объект.Товары.
ДобавляемыеРеквизиты = Новый Массив;
ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы("Доп_Примечание",
Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)), "Объект.Товары", "Примечание"));
ИзменитьРеквизиты(ДобавляемыеРеквизиты);
Колонка = Элементы.Добавить("Доп_ТоварыПримечание", Тип("ПолеФормы"), Элементы.Товары);
Колонка.Вид = ВидПоляФормы.ПолеВвода;
Колонка.ПутьКДанным = "Объект.Товары.Доп_Примечание";
Метод формы ПолучитьРеквизиты() возвращает массив РеквизитФормы верхнего уровня, а с параметром-путём — вложенные. Пройдите по нему циклом и сравните свойство Имя. Для элементов достаточно Элементы.Найти(Имя) <> Неопределено, для команд — Команды.Найти(Имя).
Элемент удаляется методом Элементы.Удалить(Элемент), команда — Команды.Удалить(Команда), реквизит — ИзменитьРеквизиты(, УдаляемыеРеквизиты), где второй параметр — массив строк с именами: "Доп_Комментарий" или "ТаблицаДанных.Цена" для колонки. Сначала убирайте элементы, которые ссылаются на реквизит, затем сам реквизит.
Реквизит формы — не реквизит объекта, сам в базу он не записывается. Если у объекта есть подходящий реквизит (свой или из расширения), перенесите значение в обработчике формы ПередЗаписьюНаСервере: ТекущийОбъект.Комментарий = ЭтотОбъект["Доп_Комментарий"]. Запись в регистр сведений по ссылке на объект делайте в ПриЗаписиНаСервере: в ПередЗаписьюНаСервере ссылка нового объекта ещё пустая, а здесь уже заполнена, и запись идёт в той же транзакции.
// Пример: непериодический регистр сведений Доп_Комментарии,
// измерение Объект, ресурс Комментарий.
// Обработчики формы назначьте в палитре свойств; если они уже есть, перенесите строки в них.
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Набор = РегистрыСведений.Доп_Комментарии.СоздатьНаборЗаписей();
Набор.Отбор.Объект.Установить(ТекущийОбъект.Ссылка); // у нового объекта ссылка здесь уже есть
ТекстКомментария = ЭтотОбъект["Доп_Комментарий"];
Если ЗначениеЗаполнено(ТекстКомментария) Тогда
Запись = Набор.Добавить();
Запись.Объект = ТекущийОбъект.Ссылка;
Запись.Комментарий = ТекстКомментария;
КонецЕсли;
Набор.Записать(); // пустой набор с отбором удалит прежнюю запись
КонецПроцедуры
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
// При открытии формы реквизита ещё нет: ПриСозданииНаСервере сработает позже
Если Элементы.Найти("Доп_Группа") <> Неопределено Тогда
Доп_ЗагрузитьКомментарий(ТекущийОбъект.Ссылка);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура Доп_ЗагрузитьКомментарий(СсылкаНаОбъект)
Значения = РегистрыСведений.Доп_Комментарии.Получить(Новый Структура("Объект", СсылкаНаОбъект));
ЭтотОбъект["Доп_Комментарий"] = Значения.Комментарий;
КонецПроцедуры
Набор записей с отбором не копит в регистре пустые строки, а стёртый комментарий удаляет прежнюю запись. В Доп_ДобавитьЭлементыПрограммно замените присваивание пустой строки вызовом Доп_ЗагрузитьКомментарий(Объект.Ссылка). Загрузка в ПриЧтенииНаСервере нужна для команды «Перечитать»: без неё на форме останется старое значение. Чтобы форма предлагала записать изменения, создавайте реквизит с СохраняемыеДанные = Истина или ставьте Модифицированность = Истина в ПриИзменении.
В типовой форме, которая регулярно обновляется, такой код может сломаться после очередного релиза: соседний элемент переименовали или в новой версии появился элемент с тем же именем, что у вашего. Доработку форм через расширение можно поручить программисту: посмотрите услуги по доработке 1С или сразу разместите задачу — оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.