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