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