Как найти и выделить строку на втором и последующих уровнях в дереве значений 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Работа с иерархическими структурами данных, такими как Дерево значений, является одной из классических задач при разработке на платформе 1С. Часто возникают ситуации, когда стандартный поиск по коллекции строк верхнего уровня не дает результата, если искомый элемент находится "глубже". В этой статье мы подробно разберем, как правильно организовать поиск на всех уровнях вложенности и как сделать найденную строку активной в экранной форме.

Проблема использования метода Найти

Рассмотрим ситуацию: у нас есть объект ДеревоЗначений, и мы пытаемся найти в нем строку по определенному значению в колонке. Типичная ошибка начинающего разработчика заключается в попытке вызвать метод Найти только у коллекции верхнего уровня. Проанализируем стандартный подход:


РезультатПоиска = ДеревоРезультата.Строки.Найти("НужноеЗначение", "ИмяКолонки", Истина);

Параметр Истина в данном методе отвечает за рекурсивный поиск. Теоретически, система должна просмотреть все уровни дерева. Однако на практике разработчики часто сталкиваются с тем, что метод возвращает Неопределено. Почему так происходит? Выясним основные причины:

  1. Несоответствие типов данных: Это самая частая причина. Если в колонке дерева хранится ссылка (например, СправочникСсылка.Номенклатура), а вы передаете в поиск строку с названием, метод ничего не найдет. Типы должны совпадать идеально.
  2. Пробелы и невидимые символы: При поиске по строковым полям лишний пробел в начале или конце значения сделает поиск безуспешным.
  3. Особенности контекста: В некоторых случаях метод может вести себя некорректно, если дерево связано с элементами формы и имеет специфические настройки отображения.

Решение через метод НайтиСтроки

Если прямой метод Найти не дает результата, наиболее надежным способом в типовом функционале 1С является использование метода НайтиСтроки. В отличие от предыдущего метода, он возвращает массив найденных строк, что позволяет обработать ситуацию, когда под критерий поиска попадает несколько элементов.

Разберем по шагам, как реализовать этот поиск:

  1. Создаем структуру поиска, где ключом является имя колонки, а значением — искомый объект.
  2. Вызываем метод НайтиСтроки с параметром рекурсии.
  3. Проверяем количество элементов в полученном массиве.

ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("ИмяЭлемента", "ИскомоеИмя");

// Второй параметр "Истина" включает поиск по всем подчиненным строкам
МассивРезультатов = ДеревоРезультата.Строки.НайтиСтроки(ПараметрыОтбора, Истина);

Если МассивРезультатов.Количество() > 0 Тогда
    // Берем первый найденный элемент
    НайденнаяСтрока = МассивРезультатов[0];
    Сообщить("Строка найдена на уровне: " + НайденнаяСтрока.Уровень());
Иначе
    Сообщить("К сожалению, поиск не дал результатов.");
КонецЕсли;

Как сделать найденную строку текущей

Найти данные в памяти — это только половина задачи. Пользователю важно увидеть результат на экране. В обычных формах (на которые указывает использование объекта ЭлементыФормы) для этого нужно присвоить найденную строку свойству ТекущаяСтрока табличного поля. Но есть нюанс: если строка находится глубоко внутри дерева, и её родители "свернуты", пользователь её не увидит, даже если она станет "текущей".

Рассмотрим комплексное решение для выделения строки:


Если МассивРезультатов.Количество() > 0 Тогда
    ТекущийЭлемент = МассивРезультатов[0];
    
    // Устанавливаем строку как текущую в визуальном элементе
    ЭлементыФормы.ДеревоРезультата.ТекущаяСтрока = ТекущийЭлемент;
    
    // Чтобы строка была видна, нужно раскрыть всех её предков
    Родитель = ТекущийЭлемент.Родитель;
    Пока Родитель <> Неопределено Цикл
        ЭлементыФормы.ДеревоРезультата.Развернуть(Родитель);
        Родитель = Родитель.Родитель;
    КонецЦикла;
КонецЕсли;

Рекурсивный обход как альтернативный способ

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


Функция НайтиСтрокуРекурсивно(КоллекцияСтрок, ИскомоеЗначение)
    Для Каждого Стр Из КоллекцияСтрок Цикл
        // Проверяем текущую строку
        Если Стр.ИмяЭлемента = ИскомоеЗначение Тогда
            Возврат Стр;
        КонецЕсли;
        
        // Ищем в подчиненных строках
        РезультатПоиска = НайтиСтрокуРекурсивно(Стр.Строки, ИскомоеЗначение);
        Если РезультатПоиска <> Неопределено Тогда
            Возврат РезультатПоиска;
        КонецЕсли;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции

Этот способ является самым гибким. Вы можете легко изменить условие Стр.ИмяЭлемента = ИскомоеЗначение на любое другое, например, используя функцию Найти() для поиска подстроки или проверяя сразу несколько колонок.

Важные замечания для Управляемых форм

Если вы работаете в Управляемом приложении, помните, что дерево на форме представлено типом ДанныеФормыДерево. У этого объекта нет метода НайтиСтроки. В этом случае алгоритм действий меняется:

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

← На главную