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

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

1С: как показать дерево значений на форме

Как вывести дерево значений на форму в 1С: реквизит формы, колонки, заполнение, обход строк, расширение и сворачивание. Примеры кода и типичные ошибки.

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

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

Материал ориентирован на управляемые формы (обычное приложение имеет свои особенности, и они упомянуты отдельно). Код приводится на встроенном языке 1С и работает на актуальных версиях платформы.

Создание реквизита формы типа «ДеревоЗначений»

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

В конфигураторе откройте форму, перейдите на закладку «Реквизиты» и добавьте реквизит, например ДеревоДанных, с типом «ДеревоЗначений». Затем через кнопку добавления колонок опишите структуру: «Наименование» (Строка), «Ссылка» (ссылка на нужный справочник), «Количество» (Число) и так далее. Колонка, по которой строится иерархия, отдельно не задаётся — вложенность определяется структурой строк дерева, а не значением колонки.

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

Добавление элемента формы для показа дерева

После создания реквизита перетащите его из панели реквизитов в дерево элементов формы. Платформа автоматически создаст элемент типа «Таблица» с видом «Дерево». Если вид установился как «Список» или «Иерархический список», измените свойство «Отображение» элемента на значение «Дерево» — только в этом режиме показываются кнопки разворачивания и вложенные строки.

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

При программном создании элемента код выглядит так: создаётся элемент через Элементы.Добавить("ДеревоДанных", Тип("ТаблицаФормы")), задаётся ПутьКДанным и Отображение = ОтображениеТаблицы.Дерево. Затем для каждой колонки добавляется элемент «ПолеФормы» с видом «Поле надписи» или «Поле ввода».

Заполнение дерева и вывод на форму

Типовой сценарий: на сервере строится объект «ДеревоЗначений», затем он переносится в реквизит формы. Заполнение обычно выполняется рекурсивной процедурой по выборке из запроса с итогами по иерархии или по обходу результата запроса с группировкой.

&НаСервере

Процедура ЗаполнитьДерево()

Дерево = Новый ДеревоЗначений;

Дерево.Колонки.Добавить("Наименование");

Дерево.Колонки.Добавить("Ссылка");

Запрос = Новый Запрос(

"ВЫБРАТЬ

| Номенклатура.Ссылка КАК Ссылка,

| Номенклатура.Наименование КАК Наименование

|ИЗ

| Справочник.Номенклатура КАК Номенклатура

|ГДЕ

| НЕ Номенклатура.ПометкаУдаления

|ИТОГИ ПО

| Номенклатура.Ссылка ТОЛЬКО ИЕРАРХИЯ");

ВыборкаГрупп = Запрос.Выполнить().Выбрать(

ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

ДобавитьСтрокиРекурсивно(Дерево.Строки, ВыборкаГрупп);

ЗначениеВДанныеФормы(Дерево, ДеревоДанных);

КонецПроцедуры

&НаСервере

Процедура ДобавитьСтрокиРекурсивно(СтрокиДерева, Выборка)

Пока Выборка.Следующий() Цикл

НоваяСтрока = СтрокиДерева.Добавить();

НоваяСтрока.Наименование = Выборка.Наименование;

НоваяСтрока.Ссылка = Выборка.Ссылка;

Вложенные = Выборка.Выбрать(

ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

ДобавитьСтрокиРекурсивно(НоваяСтрока.Строки, Вложенные);

КонецЦикла;

КонецПроцедуры

Критичный момент — строка ЗначениеВДанныеФормы(Дерево, ДеревоДанных). Именно она переносит построенное дерево в реквизит формы. Пропуск этой строки — самая частая причина пустой таблицы на форме при корректно заполненном дереве.

Для чтения данных обратно (например, при записи отредактированного дерева) используется обратное преобразование: Дерево = РеквизитФормыВЗначение("ДеревоДанных"). После этого доступны все методы объекта «ДеревоЗначений» — поиск, сортировка, обход строк.

Порядок действий
  1. 1
    Добавьте реквизит формы типа «ДеревоЗначений» и опишите его колонки
  2. 2
    Перетащите реквизит на форму и установите отображение «Дерево»
  3. 3
    На сервере постройте объект ДеревоЗначений и заполните его строки рекурсивно
  4. 4
    Выполните ЗначениеВДанныеФормы(Дерево, ИмяРеквизита)
  5. 5
    Проверьте отображение: ветки разворачиваются, колонки заполнены

Обход строк и работа с данными дерева

После вывода дерева обычно требуется обойти его строки: подсчитать итоги, найти помеченные элементы, собрать выбранные ссылки. В объекте «ДеревоЗначений» обход делается рекурсией по коллекции Строки каждой строки. Альтернатива — метод НайтиСтроки(ПараметрыОтбора, Истина): второй параметр включает поиск по всем уровням иерархии, что избавляет от ручной рекурсии при простом поиске.

Если работа ведётся прямо с реквизитом формы (без преобразования в значение), учтите: у ДанныеФормыДерево вместо коллекции «Строки» используется метод ПолучитьЭлементы(). Корневые строки — ДеревоДанных.ПолучитьЭлементы(), вложенные — СтрокаДерева.ПолучитьЭлементы(). Смешивание двух моделей обращения — частый источник ошибок «Метод объекта не обнаружен».

Текущая строка дерева на клиенте доступна через Элементы.ДеревоДанных.ТекущаяСтрока — это идентификатор строки, а не сама строка. Чтобы получить данные, используйте ДеревоДанных.НайтиПоИдентификатору(Идентификатор). Родителя строки возвращает метод ПолучитьРодителя().

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

Реквизит в конфигураторе

Подходит при фиксированной структуре колонок. Наглядно, колонки видны в редакторе формы, проще сопровождение.

Программное создание реквизита

Подходит, когда состав колонок зависит от настроек или вида отчёта. Требует аккуратности: создавать только в ПриСозданииНаСервере до отображения формы.

Динамический список вместо дерева

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

Разворачивание веток и оформление

По умолчанию после заполнения дерево показывается свёрнутым, и пользователь видит только корневые строки. Чтобы развернуть ветки программно, на клиенте используется метод Элементы.ДеревоДанных.Развернуть(ИдентификаторСтроки, Истина) — второй параметр разворачивает и все подчинённые уровни. Для разворачивания всего дерева переберите корневые строки и разверните каждую.

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

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

Типичные ошибки и их устранение

Первая группа ошибок связана с преобразованием данных: дерево заполнено, но ЗначениеВДанныеФормы не вызван, либо вызван с неверным именем реквизита — форма остаётся пустой. Вторая группа — несовпадение состава колонок: колонка есть в дереве значений, но отсутствует в реквизите формы (или наоборот), из-за чего данные теряются при переносе.

Третья частая проблема — попытка обратиться к Строки у реквизита формы на клиенте. На тонком клиенте доступна только модель ДанныеФормыДерево с методом ПолучитьЭлементы(); коллекция «Строки» существует только у объекта «ДеревоЗначений» на сервере. Четвёртая — вызов Развернуть на сервере или до отображения элемента, что даёт ошибку или молча не срабатывает.

Типичные ошибки
01
Забыли ЗначениеВДанныеФормы — дерево заполнено в памяти, но форма пустая
02
У элемента формы отображение «Список» вместо «Дерево» — иерархия и кнопки разворачивания не видны
03
Обращение к .Строки на клиенте вместо ПолучитьЭлементы() — ошибка «Метод объекта не обнаружен»
04
Несовпадение имён и типов колонок дерева и реквизита формы — данные частично теряются при переносе
05
Развернуть() вызван на сервере — метод доступен только на клиенте после отображения формы

Проверка результата

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

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

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

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

Видны корневые строки с треугольниками разворачивания, колонки заполнены

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

Таблица пустая или строки показаны плоским списком без иерархии

Разворачивание веток
Норма

Вложенные строки появляются с отступом под родителем

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

Кнопки разворачивания отсутствуют — проверьте вид отображения таблицы

Повторное заполнение
Норма

Старые строки полностью заменяются новыми, дублей нет

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

Строки добавляются к существующим — перед заполнением не очищалось дерево

Чтение данных обратно
Норма

РеквизитФормыВЗначение возвращает дерево со всеми уровнями

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

Возвращаются только корневые строки или теряются значения колонок

Часто задаваемые вопросы

Можно ли показать дерево значений в обычной форме?

Да. В обычном приложении используется элемент формы «ТабличноеПоле» с типом значения «ДеревоЗначений», и преобразования ЗначениеВДанныеФормы не требуются — реквизиту напрямую присваивается построенное дерево. Однако обычные формы применяются всё реже, и для новых разработок рекомендуется управляемый интерфейс.

Как добавить флажки в дерево значений?

Добавьте в реквизит дерева колонку с типом «Булево», а на форме создайте для неё поле с видом «Поле флажка». Для трёхсостояний флажков (когда у родителя отмечена часть подчинённых) логику установки и обратного расчёта состояния родителя придётся реализовать самостоятельно в обработчике «ПриИзменении».

Почему после ЗначениеВДанныеФормы форма всё равно пустая?

Чаще всего имя второго параметра не совпадает с именем реквизита формы, либо у элемента таблицы неверно заполнено свойство «ПутьКДанным». Также проверьте, что процедура заполнения имеет директиву &НаСервере и действительно вызывается — поставьте точку останова или сообщение о количестве строк дерева.

Как сохранить состояние развёрнутых веток при обновлении дерева?

Перед перезаполнением соберите на клиенте идентификаторы или ключи развёрнутых строк (например, значения колонки «Ссылка»), а после заполнения пройдите по новым строкам и разверните те, чьи ключи есть в сохранённом списке. Платформа автоматически это состояние не восстанавливает.

Чем дерево значений отличается от динамического списка с иерархией?

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

Как программно найти строку в дереве на нескольких уровнях?

Преобразуйте реквизит в значение через РеквизитФормыВЗначение и вызовите у дерева метод НайтиСтроки с параметром отбора и вторым аргументом Истина — поиск пройдёт по всем уровням иерархии. Либо напишите рекурсивный обход по ПолучитьЭлементы(), если работать нужно прямо на клиенте без вызова сервера.

Итог

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

Показ дерева значений строится на трёх шагах: реквизит формы типа «ДеревоЗначений», элемент таблицы с отображением «Дерево» и перенос данных через ЗначениеВДанныеФормы. На клиенте работайте через ПолучитьЭлементы(), разворачивайте ветки только на клиенте и не выводите слишком большие объёмы строк.