Разработка и Конфигурация · 13 мин
Как вывести дерево на форму в 1С
Как вывести дерево значений на форму 1С: создание реквизита, рекурсивное заполнение, работа с ДеревоЗначений и данными справочника, типичные ошибки.
Вывод иерархических данных на управляемую форму — одна из самых частых задач при разработке в 1С: структура подразделений, группы номенклатуры, план счетов, состав изделия. Платформа не позволяет разместить объект «ДеревоЗначений» напрямую на форме: сначала его нужно преобразовать в реквизит формы с типом «Дерево значений», а затем заполнить программно.
Основная сложность для начинающих разработчиков — понимание того, что дерево на форме и дерево значений в коде — это разные объекты с разными методами. Реквизит формы доступен только на сервере в контексте формы, заполняется построчно через коллекцию «ПолучитьЭлементы()», а не через «Строки», как у обычного дерева значений. Путаница между этими объектами — источник большинства ошибок.
Ниже разобран полный цикл: создание реквизита, добавление элемента на форму, рекурсивное заполнение из запроса или справочника, обработка выбора строки и типичные ошибки с их решениями.
Создание реквизита формы типа «Дерево значений»
Первый шаг — добавить в форму реквизит, который будет хранить иерархию. Откройте форму в конфигураторе, перейдите на закладку «Реквизиты» в правой части редактора формы и добавьте новый реквизит. В свойстве «Тип» выберите «ДеревоЗначений». Дайте реквизиту понятное имя, например «ДеревоНоменклатуры» или «ДеревоПодразделений».
Сразу после создания реквизита у него нет ни одной колонки. Раскройте реквизит в дереве и добавьте подчинённые реквизиты-колонки: например, «Наименование» (Строка), «Ссылка» (СправочникСсылка нужного типа), «Код», «Пометка» (Булево). Именно эти колонки будут отображаться в дереве на форме и заполняться программно.
Если реквизит создаётся программно (через «ИзменитьРеквизиты»), учитывайте, что состав колонок нужно описывать массивом добавляемых реквизитов с указанием пути — имени родительского дерева. Декларативное создание через конфигуратор проще и надёжнее, программное оправдано только когда состав колонок заранее неизвестен.
Размещение элемента «Дерево» на форме
После создания реквизита перетащите его из панели реквизитов в дерево элементов формы (левая часть редактора). Платформа автоматически создаст элемент «Таблица», связанный с реквизитом. Откройте свойства элемента и установите свойство «Отображение» в значение «Дерево» — без этого данные покажутся плоским списком без иерархии.
Полезные свойства, которые стоит настроить сразу: «ПутьКДаннымРодителя» у дерева значений на форме заполнять не требуется (в отличие от динамического списка), «Шапка» — включите, если колонок несколько, «КоманднаяПанель» — отключите, если действия над деревом не нужны. Для колонки с наименованием обычно достаточно оставить режим просмотра, установив «ТолькоПросмотр» у всей таблицы.
Проверьте результат в режиме предприятия: пустое дерево должно отображаться без ошибок. Если форма не открывается с сообщением о неверном типе данных — чаще всего причина в том, что элемент формы связан не с тем реквизитом или тип реквизита задан как «ТаблицаЗначений» вместо «ДеревоЗначений».
Заполнение дерева программно: рекурсия по строкам
Заполнение выполняется в серверной процедуре формы, обычно в обработчике «ПриСозданииНаСервере» или по команде «Обновить». Ключевой момент: у реквизита формы нет свойства «Строки» — вместо него используется метод «ПолучитьЭлементы()», возвращающий коллекцию строк текущего уровня.
Типовой шаблон рекурсивного заполнения выглядит так: сначала очищается корень дерева, затем вызывается рекурсивная процедура, которая для каждой строки источника добавляет элемент в текущую коллекцию и вызывает саму себя для дочерних строк.
&НаСервере
Процедура ЗаполнитьДеревоНаСервере()
Дерево = РеквизитФормыВЗначение("ДеревоНоменклатуры");
Дерево.Строки.Очистить();
ЗаполнитьВетку(Дерево.Строки, Справочники.Номенклатура.ПустаяСсылка());
ЗначениеВРеквизитФормы(Дерево, "ДеревоНоменклатуры");
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ЗаполнитьВетку(СтрокиПриемник, Родитель)
Запрос = Новый Запрос(
"ВЫБРАТЬ Ссылка, Наименование
|ИЗ Справочник.Номенклатура
|ГДЕ Родитель = &Родитель
|УПОРЯДОЧИТЬ ПО Наименование");
Запрос.УстановитьПараметр("Родитель", Родитель);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = СтрокиПриемник.Добавить();
НоваяСтрока.Наименование = Выборка.Наименование;
НоваяСтрока.Ссылка = Выборка.Ссылка;
ЗаполнитьВетку(НоваяСтрока.Строки, Выборка.Ссылка);
КонецЦикла;
КонецПроцедуры
Обратите внимание на пару методов: «РеквизитФормыВЗначение» преобразует реквизит формы в обычный объект «ДеревоЗначений», с которым удобно работать привычными методами, а «ЗначениеВРеквизитФормы» возвращает заполненные данные обратно на форму. Работать напрямую с реквизитом формы тоже можно, но через преобразование код получается чище.
- 1Создайте реквизит формы типа «ДеревоЗначений» и добавьте в него колонки
- 2Перетащите реквизит на форму и установите у таблицы «Отображение = Дерево»
- 3В серверной процедуре получите объект через РеквизитФормыВЗначение и очистите строки
- 4Заполните дерево рекурсивно, добавляя строки и переходя к дочерним уровням
- 5Верните данные на форму через ЗначениеВРеквизитФормы
- 6Откройте форму в режиме предприятия и проверьте раскрытие веток
Быстрое заполнение через запрос с итогами по иерархии
Рекурсия с запросом на каждый уровень проста, но медленна на больших справочниках: каждый узел порождает отдельный запрос к базе. Для иерархических справочников есть более эффективный способ — один запрос с конструкцией «ИТОГИ ПО ... ИЕРАРХИЯ» и обход результата методом «Выбрать» с типом обхода «ПоГруппировкамСИерархией».
Запрос = Новый Запрос(
"ВЫБРАТЬ Ссылка, Наименование
|ИЗ Справочник.Номенклатура
|ГДЕ НЕ ПометкаУдаления
|ИТОГИ ПО Ссылка ИЕРАРХИЯ");
РезультатЗапроса = Запрос.Выполнить();
Дерево = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
ЗначениеВРеквизитФормы(Дерево, "ДеревоНоменклатуры");
Метод «Выгрузить» с обходом по группировкам с иерархией сразу возвращает готовое дерево значений — рекурсия в коде не нужна. Ограничение: колонки результата будут названы по полям запроса, поэтому имена колонок реквизита формы должны совпадать с ними, иначе данные не перенесутся. При необходимости используйте псевдонимы в запросе («ВЫБРАТЬ Ссылка КАК Ссылка, Наименование КАК Наименование»).
Сравнение вариантов заполнения
Простой и понятный код, подходит для небольших иерархий. На крупных справочниках даёт много обращений к базе и заметную задержку
Один запрос и готовое дерево через Выгрузить. Быстрее, но требует совпадения имён колонок и работает только с иерархическими источниками
Один запрос всех записей, затем поиск родителя в дереве по ссылке. Универсально для любых источников, но код сложнее
Обработка выбора строки и разворачивание веток
Чтобы реагировать на действия пользователя, назначьте таблице дерева обработчики событий. Чаще всего используются «Выбор» (двойной щелчок или Enter — например, открыть карточку элемента по ссылке из текущей строки) и «ПриАктивизацииСтроки» (показать связанную информацию в соседней области формы).
&НаКлиенте
Процедура ДеревоНоменклатурыВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
ТекущиеДанные = Элементы.ДеревоНоменклатуры.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено
И ЗначениеЗаполнено(ТекущиеДанные.Ссылка) Тогда
ПоказатьЗначение(, ТекущиеДанные.Ссылка);
КонецЕсли;
КонецПроцедуры
Для программного разворачивания веток используется метод «Развернуть» у элемента формы с указанием идентификатора строки. Идентификатор строки (число) отличается от самой строки дерева: получить его можно через «ПолучитьИдентификатор()» у строки дерева на сервере, либо перебрать элементы коллекции «ПолучитьЭлементы()» уже на клиенте.
Если после заполнения нужно развернуть первый уровень дерева, делайте это на клиенте после обновления данных: переберите Элементы.Дерево.ПолучитьЭлементы() — точнее, коллекцию строк реквизита через ЭтаФорма.ДеревоНоменклатуры.ПолучитьЭлементы() — и для каждой строки вызовите Элементы.ДеревоНоменклатуры.Развернуть(Строка.ПолучитьИдентификатор()).
Типичные ошибки и их устранение
Самая частая ошибка — обращение к свойству «Строки» у реквизита формы: «Поле объекта не обнаружено (Строки)». У данных формы с типом дерева вместо «Строки» используется «ПолучитьЭлементы()». Обратная ситуация — вызов «ПолучитьЭлементы()» у обычного объекта «ДеревоЗначений», где есть только «Строки». Всегда отслеживайте, с каким объектом работаете в конкретной строке кода.
Вторая группа ошибок связана с контекстом выполнения. Преобразования «РеквизитФормыВЗначение» и «ЗначениеВРеквизитФормы» доступны только на сервере в модуле формы. Вызов их в клиентской процедуре приведёт к ошибке компиляции или выполнения. Правильная схема: клиентская процедура вызывает серверную, та заполняет реквизит, и форма обновляется автоматически.
Третья типичная проблема — дерево заполняется, но на форме пусто. Причины: забыли вызвать «ЗначениеВРеквизитФормы» после заполнения; заполняли другой реквизит, не тот, что привязан к таблице; у элемента формы не установлено «Отображение = Дерево» и иерархия визуально «сплющена». Проверяйте эти три пункта в первую очередь.
Проверка результата и ограничения
После реализации проверьте дерево в режиме предприятия по нескольким признакам: ветки раскрываются и сворачиваются, порядок строк соответствует сортировке, двойной щелчок открывает нужный объект, повторное обновление не дублирует строки (признак того, что забыта очистка). Также проверьте поведение на пустом источнике данных — форма не должна падать с ошибкой.
Учитывайте ограничения платформы. Дерево значений на форме не поддерживает иерархические итоги и группировки, как отчёт на СКД. При очень большом объёме данных (десятки тысяч строк) отрисовка дерева замедляет работу формы — в таких случаях лучше выводить только верхние уровни и подгружать ветки при разворачивании. Для отображения данных с произвольными отборами и быстрой навигацией иногда удобнее динамический список с иерархией вместо дерева значений.
Как проверить результат
Строки имеют вложенность, ветки раскрываются кнопкой «+»
Все строки на одном уровне — не установлено «Отображение = Дерево»
После повторного заполнения количество строк не меняется
Строки дублируются — перед заполнением не очищается дерево
Двойной щелчок открывает карточку объекта из колонки «Ссылка»
Открывается пустое значение или ничего не происходит — колонка ссылки не заполнена
Форма открывается без заметной паузы
Длительное ожидание — рекурсивные запросы на каждый узел, нужен запрос с иерархией
Часто задаваемые вопросы
Чем дерево значений отличается от таблицы значений на форме?
Таблица значений хранит плоский список строк, а дерево значений — иерархию: у каждой строки могут быть подчинённые строки неограниченной глубины. На форме это разные типы реквизитов, и элемент таблицы с отображением «Дерево» корректно работает только с реквизитом типа «ДеревоЗначений».
Почему возникает ошибка «Поле объекта не обнаружено (Строки)»?
Ошибка означает, что код обращается к свойству «Строки» у реквизита формы, а не у объекта «ДеревоЗначений». У данных формы коллекция строк получается методом «ПолучитьЭлементы()». Либо замените обращение, либо сначала преобразуйте реквизит в объект через «РеквизитФормыВЗначение».
Можно ли вывести дерево на форму без рекурсии?
Да. Для иерархических справочников используйте запрос с конструкцией «ИТОГИ ПО Ссылка ИЕРАРХИЯ» и выгрузку результата с обходом «ПоГруппировкамСИерархией» — получится готовое дерево одним запросом. Для произвольных источников можно построить дерево из плоской выборки, находя родителя поиском по колонке ссылки.
Как развернуть все ветки дерева программно?
На клиенте переберите строки верхнего уровня через «ПолучитьЭлементы()» и для каждой вызовите «Развернуть» у элемента формы, передав идентификатор строки из метода «ПолучитьИдентификатор()». Разворачивание работает по одному уровню, поэтому для полного раскрытия вызов повторяют рекурсивно по подчинённым строкам.
Что выбрать: дерево значений или динамический список с иерархией?
Дерево значений подходит, когда состав данных формируется программно, нужна произвольная структура или смешанные данные из разных источников. Динамический список с иерархическим отображением лучше для больших справочников: он сам подгружает данные порциями и поддерживает стандартные отборы и поиск без дополнительного кода.
Как запомнить развёрнутые ветки после обновления дерева?
Платформа не сохраняет состояние развёртывания автоматически. Перед очисткой дерева соберите значения ключевой колонки (например, ссылки) развёрнутых строк, а после заполнения найдите соответствующие строки и разверните их заново. Для этого удобно хранить список значений в реквизите формы.
Что важно запомнить
Дерево выводится на форму через реквизит типа «ДеревоЗначений» и таблицу с отображением «Дерево». Заполнение выполняется на сервере: реквизит преобразуется в объект через РеквизитФормыВЗначение, заполняется рекурсивно или запросом с иерархией и возвращается через ЗначениеВРеквизитФормы. У реквизита формы строки получают методом ПолучитьЭлементы(), а не через свойство Строки.