Как выполнить отбор и фильтрацию в реквизите типа ДеревоЗначений в 1С 8.3

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Суть проблемы: ДанныеФормыДерево против ДереваЗначений

Часто начинающие разработчики сталкиваются с ошибкой "Поле объекта не обнаружено (Строки)" или отсутствием метода НайтиСтроки(). Это происходит потому, что реквизит формы имеет тип ДанныеФормыДерево, который является лишь "отображением" данных и не обладает всеми методами объектного типа ДеревоЗначений. Чтобы полноценно работать с коллекцией (искать, фильтровать, удалять), нам необходимо преобразовать реквизит формы в объектную сущность, выполнить операции и вернуть результат обратно.

Алгоритм реализации отбора

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

  1. Преобразование в объект: Используем функцию РеквизитФормыВЗначение, чтобы получить полноценное ДеревоЗначений.
  2. Получение эталона: Формируем запрос или создаем вспомогательную структуру (например, Соответствие) с теми данными, которые должны остаться.
  3. Поиск и фильтрация: Обходим дерево и определяем, какие строки соответствуют условию.
  4. Загрузка обратно: Используем ЗначениеВРеквизитФормы для обновления интерфейса.

Способ 1: Использование метода НайтиСтроки

Этот метод удобен, когда нужно быстро найти строки по точному совпадению значений в колонках. Рассмотрим пример кода, который демонстрирует этот подход:


// Переводим данные формы в объектное дерево
ДеревоОбъект = РеквизитФормыВЗначение("Товары");

// Допустим, у нас есть ТЗРезультатЗапроса с нужной номенклатурой
ПараметрыОтбора = Новый Структура;
Для Каждого СтрокаТЗ Из ТЗРезультатЗапроса Цикл
    ПараметрыОтбора.Вставить("Номенклатура", СтрокаТЗ.Номенклатура);
    ПараметрыОтбора.Вставить("Характеристика", СтрокаТЗ.Характеристика);
    
    // Ищем рекурсивно по всему дереву
    НайденныеСтроки = ДеревоОбъект.Строки.НайтиСтроки(ПараметрыОтбора, Истина);
    
    // Дальнейшая логика по копированию найденных строк в новое дерево
КонецЦикла;

// Возвращаем результат на форму
ЗначениеВРеквизитФормы(ДеревоОбъект, "Товары");

Важный нюанс: в методе НайтиСтроки(ПараметрыОтбора, ИскатьРекурсивно) второй параметр Истина позволяет искать узлы на всех уровнях вложенности. Если его не указать, поиск ограничится только верхним уровнем.

Способ 2: Рекурсивное удаление лишних строк (снизу вверх)

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


Процедура ФильтроватьВетки(КоллекцияСтрок, ТЗЭталон)
    
    Индекс = КоллекцияСтрок.Количество() - 1;
    
    Пока Индекс >= 0 Цикл
        ТекущаяСтрока = КоллекцияСтрок[Индекс];
        
        // Сначала идем вглубь (рекурсия)
        ФильтроватьВетки(ТекущаяСтрока.Строки, ТЗЭталон);
        
        // Проверяем, соответствует ли строка условию
        ПараметрыПоиска = Новый Структура("Номенклатура, Характеристика", ТекущаяСтрока.Номенклатура, ТекущаяСтрока.Характеристика);
        СтрокаНайдена = (ТЗЭталон.НайтиСтроки(ПараметрыПоиска).Количество() > 0);
        
        // Условие удаления: строки нет в эталоне И у нее нет подходящих подчиненных строк
        Если Не СтрокаНайдена И ТекущаяСтрока.Строки.Количество() = 0 Тогда
            КоллекцияСтрок.Удалить(ТекущаяСтрока);
        КонецЕсли;
        
        Индекс = Индекс - 1;
    КонецЦикла;
    
КонецПроцедуры

В данном примере мы используем цикл Пока с уменьшением индекса, что позволяет безопасно удалять элементы коллекции, не вызывая смещения, которое могло бы привести к пропуску строк.

Способ 3: Оптимизация через Соответствие (Map)

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

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


// Подготовка индекса для быстрого поиска
КлючиДоступности = Новый Соответствие;
Для Каждого Стр Из ТЗРезультатЗапроса Цикл
    Ключ = Строка(Стр.Номенклатура.УникальныйИдентификатор()) + "_" + Строка(Стр.Характеристика.УникальныйИдентификатор());
    КлючиДоступности.Вставить(Ключ, Истина);
КонецЦикла;

// Теперь при обходе дерева проверка будет мгновенной
КлючСтроки = Строка(ТекущаяСтрока.Номенклатура.УникальныйИдентификатор()) + "_" + Строка(ТекущаяСтрока.Характеристика.УникальныйИдентификатор());
Если КлючиДоступности[КлючСтроки] = Неопределено Тогда
    // Строка не прошла фильтр
КонецЕсли;

Сохранение состояния дерева (развернутые узлы)

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

  1. Перед фильтрацией создадим массив и сохраним в него идентификаторы строк, которые сейчас развернуты (используя метод ДанныеФормыДерево.ПолучитьЭлементы()).
  2. Выполним фильтрацию и загрузку данных.
  3. После загрузки программно пройдемся по дереву и применим метод Развернуть() для тех узлов, чьи идентификаторы (или ключи данных) сохранились в массиве.

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

Визуальная фильтрация через Условное оформление

Рассмотрим альтернативный метод. Если удаление строк не является обязательным требованием, и нам нужно просто скрыть лишнее от глаз пользователя, можно использовать Условное оформление управляемой формы.

Для этого добавим в дерево служебную колонку Видимость (тип Булево). Пройдем циклом по дереву и установим Ложь для тех строк, которые не прошли фильтр. В настройках условного оформления формы укажем правило: если Видимость = Ложь, то установить свойство Видимость (или Высота = 1, если полное скрытие работает некорректно в вашей версии платформы) для всей строки.

Этот способ работает быстрее всего, так как не требует сложных манипуляций с объектной моделью и не сбрасывает состояние развернутых веток дерева.

Резюме

Выбор метода зависит от объема данных и требований к интерфейсу:

← На главную