К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как программно скопировать элемент формы в 1С

Как программно скопировать элемент формы в 1С: методы Скопировать(), работа с коллекцией Элементы, копирование колонок таблицы, типичные ошибки и примеры кода на встроенном языке.

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

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

Ниже разобраны оба подхода — штатное копирование методом Скопировать() и ручной перенос свойств, а также копирование колонок таблиц, работа с данными, типичные ошибки и способы проверки результата.

Где и когда допустимо изменять состав элементов формы

Управляемая форма в 1С строится декларативно: структура элементов формируется на сервере, после чего передаётся клиенту для отображения. Поэтому добавление и копирование элементов корректно работает в серверном контексте — в обработчиках ПриСозданииНаСервере, ПриЧтенииНаСервере и в серверных процедурах, вызываемых до фактического показа формы.

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

Копирование элемента методом Скопировать()

Коллекция Элементы управляемой формы поддерживает метод Скопировать(), который создаёт новый элемент на основе существующего. Синтаксис:

НовыйЭлемент = Элементы.Скопировать("ПолеКонтрагентКопия", Элементы.ПолеКонтрагент, Элементы.ГруппаРеквизиты);

Первый параметр — уникальное имя нового элемента, второй — копируемый элемент (объект, а не строка), третий — необязательный родитель, в который будет помещена копия. Если родитель не указан, элемент попадает в того же родителя, что и оригинал. Метод переносит свойства элемента и его подчинённую структуру — например, при копировании группы копируются и вложенные в неё элементы.

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

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

    НовыйЭлемент = Элементы.Скопировать("ПолеКомментарийКопия", Элементы.ПолеКомментарий, Элементы.ГруппаОсновное);
    НовыйЭлемент.ПутьКДанным = "КомментарийКопия";
    НовыйЭлемент.Заголовок = "Комментарий (копия)";
КонецПроцедуры

Копирование с привязкой к данным: реквизиты формы

Скопированный элемент изначально ссылается на тот же путь к данным, что и оригинал. Если оставить всё как есть, оба поля будут редактировать один и тот же реквизит — изменение в одном поле отобразится в другом. Для самостоятельной копии нужно сначала программно добавить реквизит формы, а затем назначить его новому элементу.

Добавление реквизитов выполняется методом ИзменитьРеквизиты(), которому передаются массивы добавляемых и удаляемых реквизитов типа РеквизитФормы. У реквизита задаются имя, тип (ОписаниеТипов), путь (для вложенных структур) и признак сохраняемости. После изменения состава реквизитов новому элементу назначается ПутьКДанным, указывающий на созданный реквизит.

Порядок действий критичен: сначала ИзменитьРеквизиты(), затем копирование или создание элемента и назначение ПутьКДанным. Обратный порядок вызовет ошибку, поскольку элемент не сможет привязаться к несуществующим данным.

Порядок действий
  1. 1
    Определите, какой элемент нужно скопировать, и подготовьте уникальное имя копии
  2. 2
    Добавьте реквизит формы через ИзменитьРеквизиты() с нужным типом данных
  3. 3
    Вызовите Элементы.Скопировать() с указанием имени, исходного элемента и родителя
  4. 4
    Назначьте новому элементу ПутьКДанным, заголовок и при необходимости обработчики через УстановитьДействие()
  5. 5
    Откройте форму и проверьте, что копия отображается, редактируется независимо и не ломает исходный элемент

Ручное создание элемента с переносом свойств

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

НовыйЭлемент = Элементы.Добавить("ПолеСумма2", Тип("ПолеФормы"), Элементы.ГруппаИтоги);

НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;

НовыйЭлемент.ПутьКДанным = "Сумма2";

НовыйЭлемент.Заголовок = Элементы.ПолеСумма.Заголовок + " (копия)";

НовыйЭлемент.КнопкаОчистки = Элементы.ПолеСумма.КнопкаОчистки;

Этот способ длиннее, но даёт полный контроль: вы явно задаёте вид поля, привязку к данным и только те свойства, которые действительно нужны. Он же единственно доступен, когда копировать нечего — например, элемент создаётся «с нуля» по описанию из метаданных или настроек пользователя.

Сравнение вариантов

Элементы.Скопировать()

Быстро, переносит все свойства и подчинённые элементы. Подходит для точных дубликатов. Ограничение: копируются и ненужные настройки, которые приходится сбрасывать

Элементы.Добавить() с ручным переносом

Полный контроль над свойствами, подходит для создания элементов по метаданным. Требует явного задания вида, типа и привязки — больше кода и выше риск что-то упустить

Копирование колонок таблицы и динамических списков

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

Для каждого Склад Из СписокСкладов Цикл

ИмяРеквизита = "Количество_" + Склад.Код;

// реквизит добавляется в таблицу значений — реквизит формы

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

НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;

НоваяКолонка.ПутьКДанным = "ТаблицаТовары." + ИмяРеквизита;

НоваяКолонка.Заголовок = Склад.Наименование;

КонецЦикла;

Здесь есть тонкость: реквизит добавляется не в корень формы, а в таблицу значений, являющуюся реквизитом формы. Для этого в конструкторе РеквизитФормы указывается путь — имя таблицы. Путь к данным колонки тогда строится как ИмяТаблицы.ИмяРеквизита.

Для динамических списков программное добавление колонок ограничено: список строится по запросу, и произвольные колонки без соответствующих полей запроса отображать нечего. Обычно меняют текст запроса динамического списка и затем добавляют колонки под новые поля.

Назначение обработчиков скопированному элементу

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

НовыйЭлемент.УстановитьДействие("ПриИзменении", "Подключаемый_ПолеПриИзменении");

Поскольку один обработчик обычно обслуживает несколько однотипных элементов, внутри него источник события определяется по параметру Элемент и его имени. Заранее продумайте соглашение об именовании, чтобы из имени элемента можно было извлечь ключ — например, код склада или номер периода.

Удобно хранить соответствие между именами динамических элементов и ключами данных (складами, периодами) в реквизите формы или в имени элемента через разделитель. Это избавляет от поиска по метаданным в каждом обработчике и упрощает отладку.
ПЗ
Практическое замечание

Проверка результата и типичные ошибки

После внедрения кода откройте форму и проверьте: копия отображается в нужном родителе, редактируется независимо от оригинала, заголовок и подсказка корректны, обработчики срабатывают. Дополнительно проверьте сохранение и повторное открытие объекта, если реквизиты связаны с данными объекта.

Контрольные точки

Как проверить результат

Отображение копии
Норма

Элемент виден в заданной группе с нужным заголовком

Тревожный признак

Элемент отсутствует или попал не в того родителя — проверьте третий параметр Скопировать()

Независимость данных
Норма

Изменение копии не меняет оригинал

Тревожный признак

Оба поля меняются синхронно — ПутьКДанным не переназначен

Повторное открытие формы
Норма

Форма открывается без ошибок

Тревожный признак

Ошибка «элемент уже существует» — нет проверки на повторное создание

Типичные ошибки
01
Копирование без переназначения ПутьКДанным — оба поля редактируют один реквизит, данные затирают друг друга
02
Попытка добавить элемент в клиентском обработчике — ошибка выполнения или отсутствие эффекта; изменения структуры допустимы только на сервере
03
Назначение ПутьКДанным до вызова ИзменитьРеквизиты() — ошибка привязки к несуществующему реквизиту
04
Повторное создание элементов без проверки существования — исключение «элемент с таким именем уже существует» при повторном вызове процедуры

Ограничения и альтернативные подходы

Метод Скопировать() доступен только для управляемых форм. В обычных формах (толстый клиент, старые конфигурации) элементы добавляются иначе — через коллекцию ЭлементыФормы с указанием типа элемента управления, а прямого аналога копирования нет: свойства переносятся вручную.

Если динамических элементов много и их состав известен заранее, часто рациональнее не копировать элементы программно, а создать их в конфигураторе и управлять видимостью. Программное создание оправдано, когда набор элементов зависит от данных — количества складов, периодов, настроек пользователя — и заранее не определён.

Маршрут решения

Как выбрать действие

Нужна точная копия элемента со всеми настройкамиИспользуйте Элементы.Скопировать() и донастройте ПутьКДанным и заголовок
Нужен элемент с выборочными свойствами или по метаданнымСоздайте элемент через Элементы.Добавить() и задайте свойства вручную
Состав элементов известен заранее и постояненСоздайте элементы в конфигураторе и управляйте видимостью вместо программного копирования
Нужны колонки таблицы по списку значенийДобавляйте реквизиты в таблицу формы через ИзменитьРеквизиты() и создавайте колонки в цикле
Итог

Что важно запомнить

Копирование элемента формы выполняется методом Элементы.Скопировать() на сервере до отображения формы. После копирования обязательно переназначьте ПутьКДанным на предварительно созданный реквизит, настройте заголовок и обработчики. Для выборочного переноса свойств используйте Элементы.Добавить() с ручным заполнением.

Частые вопросы

Можно ли скопировать элемент формы на клиенте?

Нет. Изменение состава элементов управляемой формы выполняется только на сервере. В клиентском коде можно управлять видимостью, доступностью и значениями существующих элементов, но не создавать или копировать их.

Почему скопированное поле редактирует те же данные, что и оригинал?

Метод Скопировать() переносит свойство ПутьКДанным без изменений. Чтобы копия работала с отдельными данными, добавьте новый реквизит формы через ИзменитьРеквизиты() и назначьте его элементу в свойстве ПутьКДанным.

Как скопировать группу вместе со всеми вложенными элементами?

Достаточно вызвать Скопировать() для самой группы — подчинённые элементы копируются вместе с ней. Учтите, что все вложенные копии унаследуют пути к данным оригиналов, поэтому после копирования пройдитесь по ним и переназначьте привязки.

Что делать при ошибке «элемент с таким именем уже существует»?

Проверьте, не вызывается ли процедура создания элементов повторно. Перед копированием убедитесь, что элемент с таким именем отсутствует: Элементы.Найти("ИмяЭлемента") = Неопределено. Также используйте уникальный префикс для динамических элементов.

Работает ли Скопировать() в обычных формах?

Нет, метод доступен только в управляемых формах. В обычных формах элементы создаются через ЭлементыФормы.Добавить() с указанием типа элемента управления, а нужные свойства переносятся вручную по одному.

Как назначить обработчик события программно созданному элементу?

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