При разработке решений на платформе 1С:Предприятие 8.3 в режиме управляемого приложения, часто возникает задача динамического добавления элементов управления на форму, например, колонок в табличную часть. Эта операция требует четкого понимания работы с реквизитами и элементами формы, а также внимательности к правилам формирования их имен. В этой статье мы подробно разберем, как реализовать программное добавление колонок в табличную часть формы в цикле, основываясь на опыте коллег и распространенных ошибках.
Представим ситуацию: на форме документа (управляемые формы) у нас есть две табличные части (ТЧ). Задача состоит в том, чтобы строки первой таблицы преобразовать в колонки второй, при этом значения этих строк должны стать заголовками новых колонок. Мы уже подготовили массив с именами будущих колонок и теперь хотим пройти по нему в цикле, чтобы программно создать соответствующие реквизиты и элементы формы.
Рассмотрим типовой сценарий, с которым сталкиваются разработчики. Изначально, пытаясь динамически добавить реквизиты и элементы, мы можем столкнуться с ошибками, связанными с некорректной передачей параметров. Давайте посмотрим на фрагмент кода, который демонстрирует эту проблему:
МассивДобавляемыхРеквизитов = Новый Массив;
Для Каждого тЭлемент Из мсДанных Цикл
// массив мсДанных содержит названия колонок, которые нужно добавить
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("тЭлемент", // Вот здесь нужна переменная
ОписаниеТиповДляРеквизита,
"Объект.ТЗвторая",
тЭлемент,
Ложь));
КонецЦикла;
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);
Для Каждого тЭлемент Из мсДанных Цикл
нЭлемент = ЭтаФорма.Элементы.Добавить("тЭлемент", // и вот здесь нужна переменная
Тип("ПолеФормы"),
Элементы.УстановкаДоступности); // или другой родительский элемент
нЭлемент.Вид = ВидПоляФормы.ПолеВвода;
нЭлемент.ПутьКДанным = "Объект.ТЗвторая.тЭлемент"; // путь? вариант "Объект.ТЗвторая." + тЭлемент не проходит
нЭлемент.Видимость = Истина;
КонецЦикла;
При выполнении такого кода, мы, скорее всего, получим ошибку "Неверно указан параметр "1"". Выясним причину этой ошибки и как ее исправить.
Основная проблема в первом фрагменте кода заключается в том, что мы передаем строковый литерал "тЭлемент" в качестве первого параметра конструктора Новый РеквизитФормы и метода Элементы.Добавить. Эти методы ожидают имя реквизита или элемента, которое должно быть уникальным для каждого создаваемого объекта. Когда мы передаем "тЭлемент" в кавычках, 1С воспринимает это как буквальное имя "тЭлемент", а не как значение переменной тЭлемент из цикла. В результате, при второй и последующих итерациях цикла, система пытается добавить реквизит или элемент с уже существующим именем "тЭлемент", что приводит к ошибке о неуникальности имени или неверном параметре.
Для исправления этой ситуации, нам необходимо передавать саму переменную тЭлемент (без кавычек) в качестве имени. Это позволит динамически формировать имена реквизитов и элементов на основе значений, хранящихся в переменной тЭлемент на каждой итерации цикла.
// Корректная передача переменной вместо строкового литерала
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(тЭлемент, // Теперь 'тЭлемент' без кавычек
ОписаниеТиповДляРеквизита,
"Объект.ТЗвторая",
тЭлемент,
Ложь));
// Аналогично для добавления элемента
нЭлемент = ЭтаФорма.Элементы.Добавить(тЭлемент, // 'тЭлемент' без кавычек
Тип("ПолеФормы"),
Элементы.Объект.ТЗвторая); // Указываем правильного родителя
После этой корректировки мы можем столкнуться с новой ошибкой: "Недопустимое значение параметра (параметр номер '1')". Это указывает на то, что проблема не только в передаче переменной, но и в содержимом самой переменной.
Главная и наиболее частая причина ошибки "Недопустимое значение параметра (параметр номер '1')" при динамическом добавлении реквизитов или элементов заключается в том, что значения, содержащиеся в переменной тЭлемент (которые мы используем как имя), не соответствуют требованиям к идентификаторам 1С. Имена реквизитов и элементов 1С должны:
_).-) и других специальных символов.В нашем случае, массив мсДанных, из которого берется тЭлемент, предположительно содержит строковые значения (например, заголовки колонок из первой ТЧ), которые могут включать пробелы, специальные символы или начинаться с цифр. Например, строка "Это вот такой красивый заголовок" не может быть использована как имя реквизита или элемента напрямую.
Здесь важно четко разделять понятия имени реквизита/элемента и его заголовка. Имя – это внутренний идентификатор, используемый программно, который должен быть валидным согласно правилам 1С. Заголовок – это текст, который отображается пользователю и может быть произвольным, содержать пробелы и спецсимволы.
Наша задача – создать валидное имя для программного использования и установить желаемый текст из исходной строки в качестве заголовка элемента формы.
Чтобы решить проблему с недопустимыми символами, мы должны программно очистить или преобразовать исходные строковые значения из мсДанных в валидные имена. Это можно сделать с помощью функции СтрЗаменить.
Нам потребуется функция СтрЗаменить для удаления или замены нежелательных символов. Например, заменим все пробелы на символ подчеркивания, а другие специальные символы можно просто удалить или заменить на _.
Функция СформироватьВалидноеИмя(ИсходнаяСтрока)
// Преобразуем исходную строку в валидное имя реквизита/элемента
// 1. Заменяем пробелы на подчеркивания
ПеременноеИмя = СтрЗаменить(ИсходнаяСтрока, " ", "_");
// 2. Дополнительно обрабатываем другие потенциально невалидные символы
ПеременноеИмя = СтрЗаменить(ПеременноеИмя, "-", "_"); // Заменим дефисы
ПеременноеИмя = СтрЗаменить(ПеременноеИмя, ".", "_"); // Заменим точки
ПеременноеИмя = СтрЗаменить(ПеременноеИмя, "(", "_"); // Заменим скобки
ПеременноеИмя = СтрЗаменить(ПеременноеИмя, ")", "_");
ПеременноеИмя = СтрЗаменить(ПеременноеИмя, "/", "_");
ПеременноеИмя = СтрЗаменить(ПеременноеИмя, "\", "_");
// Можно добавить другие замены или использовать регулярные выражения для более сложной очистки
// Убедимся, что имя не начинается с цифры или подчеркивания (если это нежелательно)
// Например, если имя начинается с цифры, добавим префикс
Если ПеременноеИмя = "" Или Не (Сред(ПеременноеИмя, 1, 1) Соответствует "Б") Тогда // "Б" - это просто проверка, что первый символ буква
Если ПеременноеИмя = "" Тогда
ПеременноеИмя = "Колонка"; // Если после очистки пусто, даем дефолтное имя
Иначе
Если СтрНайти("0123456789_", Сред(ПеременноеИмя, 1, 1)) > 0 Тогда // Если начинается с цифры или подчеркивания
ПеременноеИмя = "Колонка" + ПеременноеИмя;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат ПеременноеИмя;
КонецФункции
Мы используем "Б" как символ для проверки, чтобы убедиться, что строка начинается с буквы, так как функция Соответствует работает с регулярными выражениями. В данном случае, это упрощенная проверка. Более надежный способ — проверить, что первый символ не является цифрой или символом, запрещенным в начале имени.
Вместо универсального префикса "Колонка" можно также использовать более уникальные идентификаторы, например, на основе Новый УникальныйИдентификатор(), если содержимое исходной строки не принципиально для программного имени, а важно только для заголовка. Например:
ИмяКолонки = "Колонка_" + СтрЗаменить(Новый УникальныйИдентификатор(), "-", "_");
Такой подход гарантирует уникальность и валидность имени.
После создания элемента формы, его заголовок устанавливается через свойство Заголовок. Это позволяет нам использовать исходный текст из мсДанных без каких-либо изменений, сохраняя его оригинальное форматирование и содержание.
нЭлемент.Заголовок = ИсходныйТекстИзМсДанных; // Исходный текст, который должен быть заголовком
Свойство ПутьКДанным у элемента формы связывает этот элемент с конкретным реквизитом формы. Если мы динамически добавляем реквизиты к табличной части, то путь должен быть составлен с учетом имени этого реквизита. Используем конкатенацию строк для формирования пути:
нЭлемент.ПутьКДанным = "Объект.ТЗвторая." + ИмяВалиднойКолонки;
Здесь ИмяВалиднойКолонки — это то имя, которое мы сформировали на предыдущем шаге (после очистки от недопустимых символов).
Теперь, собрав все исправленные части вместе, мы можем написать рабочий код для динамического добавления колонок. Предположим, что мсДанных – это массив строк, содержащих будущие заголовки колонок, ОписаниеТиповДляРеквизита – это заранее подготовленный объект ОписаниеТипов для новых реквизитов (например, Новый ОписаниеТипов("Строка")), а Элементы.Объект.ТЗвторая – это элемент формы, представляющий табличную часть, куда мы добавляем колонки.
// Предполагаем, что этот код выполняется на сервере
// Например, в обработчике ПриСозданииНаСервере или в отдельной серверной процедуре
// Шаг 1: Подготавливаем описание типов для новых реквизитов (например, строка)
ОписаниеТиповДляРеквизита = Новый ОписаниеТипов("Строка");
// Шаг 2: Создаем массив для добавления реквизитов формы
МассивДобавляемыхРеквизитов = Новый Массив;
// Шаг 3: Массив для хранения соответствия ИсходногоТекста и ВалидногоИмени
СоответствиеИменКолонок = Новый Соответствие;
// Цикл по исходным данным (например, строкам из первой ТЧ)
Для Каждого ИсходныйЗаголовок Из мсДанных Цикл
// Формируем валидное имя для реквизита и элемента
// Сначала очищаем от пробелов и других нежелательных символов
ВалидноеИмяКолонки = СтрЗаменить(ИсходныйЗаголовок, " ", "_");
ВалидноеИмяКолонки = СтрЗаменить(ВалидноеИмяКолонки, "-", "_");
ВалидноеИмяКолонки = СтрЗаменить(ВалидноеИмяКолонки, ".", "_");
ВалидноеИмяКолонки = СтрЗаменить(ВалидноеИмяКолонки, "(", "_");
ВалидноеИмяКолонки = СтрЗаменить(ВалидноеИмяКолонки, ")", "_");
ВалидноеИмяКолонки = СтрЗаменить(ВалидноеИмяКолонки, "/", "_");
ВалидноеИмяКолонки = СтрЗаменить(ВалидноеИмяКолонки, "\", "_");
// Добавляем префикс, если имя пустое или начинается с цифры/подчеркивания
Если ВалидноеИмяКолонки = "" Или СтрНайти("0123456789_", Сред(ВалидноеИмяКолонки, 1, 1)) > 0 Тогда
ВалидноеИмяКолонки = "Колонка" + ВалидноеИмяКолонки;
КонецЕсли;
// Проверяем на уникальность (очень важно!)
Если СоответствиеИменКолонок.Получить(ВалидноеИмяКолонки) <> Неопределено Тогда
// Если имя уже существует, добавляем суффикс для уникальности
ВалидноеИмяКолонки = ВалидноеИмяКолонки + "_" + СтрЗаменить(Новый УникальныйИдентификатор(), "-", "_");
КонецЕсли;
СоответствиеИменКолонок.Вставить(ВалидноеИмяКолонки, ИсходныйЗаголовок); // Сохраняем для второго цикла и для заголовка
// Добавляем новый реквизит формы
// Имя реквизита - ВалидноеИмяКолонки (без кавычек)
// Родитель - "Объект.ТЗвторая"
МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы(ВалидноеИмяКолонки,
ОписаниеТиповДляРеквизита,
"Объект.ТЗвторая",
ИсходныйЗаголовок, // Это может быть неиспользуемым параметром здесь, но сохраним для примера
Ложь));
КонецЦикла;
// Шаг 4: Изменяем реквизиты формы (добавляем новые колонки в данные ТЧ)
Если МассивДобавляемыхРеквизитов.Количество() > 0 Тогда
ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);
КонецЕсли;
// Шаг 5: Добавляем элементы формы (визуальные колонки)
Для Каждого ЭлементСоответствия Из СоответствиеИменКолонок Цикл
ВалидноеИмяКолонки = ЭлементСоответствия.Ключ;
ИсходныйЗаголовок = ЭлементСоответствия.Значение;
// Добавляем новый элемент формы (ПолеФормы)
нЭлемент = ЭтаФорма.Элементы.Добавить(ВалидноеИмяКолонки,
Тип("ПолеФормы"),
Элементы.Объект.ТЗвторая); // Родительский элемент - сама табличная часть
нЭлемент.Вид = ВидПоляФормы.ПолеВвода;
нЭлемент.ПутьКДанным = "Объект.ТЗвторая." + ВалидноеИмяКолонки; // Путь к данным, сформированный динамически
нЭлемент.Заголовок = ИсходныйЗаголовок; // Устанавливаем исходный текст в качестве заголовка
нЭлемент.Видимость = Истина;
КонецЦикла;
ИзменитьРеквизиты) и элементов формы (Элементы.Добавить) должны выполняться на сервере. Обычно это делается в обработчиках событий формы, таких как ПриСозданииНаСервере, или в отдельных серверных процедурах, вызываемых с клиента.
Элементы.Удалить(ИмяЭлемента).ИзменитьРеквизиты массив объектов РеквизитФормы, у которых установлено свойство Удаляемый = Истина. Для этого вам потребуется сохранить список добавленных реквизитов.
// Пример удаления всех динамически добавленных реквизитов и элементов
// Предполагаем, что у вас есть массив или соответствие с именами ранее добавленных элементов
Для Каждого ИмяУдаляемогоЭлемента Из СписокДинамическихЭлементов Цикл
Если ЭтаФорма.Элементы.Найти(ИмяУдаляемогоЭлемента) <> Неопределено Тогда
ЭтаФорма.Элементы.Удалить(ИмяУдаляемогоЭлемента);
КонецЕсли;
КонецЦикла;
МассивУдаляемыхРеквизитов = Новый Массив;
Для Каждого ИмяУдаляемогоРеквизита Из СписокДинамическихРеквизитов Цикл
// Создаем фиктивный реквизит с нужным именем и помечаем его как удаляемый
МассивУдаляемыхРеквизитов.Добавить(Новый РеквизитФормы(ИмяУдаляемогоРеквизита,
Неопределено, // Тип не важен для удаления
"Объект.ТЗвторая",
Неопределено,
Истина)); // Указываем Удаляемый = Истина
КонецЦикла;
ИзменитьРеквизиты(МассивУдаляемыхРеквизитов);
Таким образом, программное добавление колонок в табличную часть формы в 1С (УФ) в цикле становится выполнимой задачей, если мы учитываем правила формирования имен реквизитов и элементов, корректно указываем пути к данным и отделяем внутренние имена от отображаемых заголовков. Помните о необходимости очистки имен от недопустимых символов и проверке на уникальность, чтобы обеспечить стабильную работу вашего решения.