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