Как программно свернуть или развернуть дерево значений в 1С

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

Работа с иерархическими структурами данных, такими как Дерево значений, является повседневной задачей разработчика 1С. Однако программное управление состоянием узлов (развертывание и сворачивание) имеет свои тонкости в зависимости от типа форм (обычные или управляемые) и версии платформы. В данной статье мы подробно разберем, как управлять визуальным представлением дерева, проанализируем различные подходы — от классической рекурсии до использования библиотек стандартных подсистем (БСП) и оптимизационных «хаков».

Различия в подходах: Обычные и Управляемые формы

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

Решение для обычных форм (Платформа 8.1, 8.2)

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


// Развернуть выделенные строки
Для каждого Строка Из ЭлементыФормы.ТабличноеПоле.ВыделенныеСтроки Цикл
    ЭлементыФормы.ТабличноеПоле.Развернуть(Строка, Истина); // Истина - развернуть с подчиненными
КонецЦикла;

// Свернуть все корневые строки дерева
Для каждого Строка Из ЭлементыФормы.ТабличноеПоле.Значение.Строки Цикл
    ЭлементыФормы.ТабличноеПоле.Свернуть(Строка);
КонецЦикла;

Обратите внимание на параметр Истина в методе Развернуть. Он позволяет рекурсивно раскрыть всю ветку. У метода Свернуть такого параметра нет, поэтому для полной свертки иерархии на большую глубину потребуется рекурсивный обход.

Решение для управляемых форм (Платформа 8.3)

В управляемых формах работа строится иначе. Нам необходимо получить идентификатор строки коллекции. Рассмотрим самый простой способ развернуть все узлы дерева одним вызовом:


&НаКлиенте
Процедура РазвернутьВсеУзлы(Команда)
    // Разворачиваем корень (индекс 0) со всеми подчиненными
    Элементы.ДеревоДанных.Развернуть(0, Истина);
КонецПроцедуры

Однако этот метод не всегда срабатывает корректно, если дерево динамически обновляется. В таких случаях разберем универсальный рекурсивный алгоритм, который гарантированно обходит все уровни:


&НаКлиенте
Процедура УправлениеДеревом(Развернуть = Истина)
    КоллекцияЭлементов = ДеревоДанных.ПолучитьЭлементы();
    ВыполнитьОбход(КоллекцияЭлементов, Развернуть);
КонецПроцедуры

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

Важный нюанс при сворачивании: проанализируем ситуацию в сообщении №31. Практика показывает, что на некоторых версиях платформы (например, 8.3.18) узел может не свернуться, если сначала не были свернуты его дочерние элементы. Поэтому в примере выше при Развернуть = Ложь мы сначала уходим в рекурсию и только потом вызываем Свернуть() для родителя.

Использование Библиотеки стандартных подсистем (БСП)

Если ваша конфигурация построена на базе БСП, не стоит изобретать велосипед. Проанализируем возможности общего модуля СтандартныеПодсистемыКлиент. В нем существует готовый метод, который учитывает все особенности платформы:


// Развернуть дерево полностью
СтандартныеПодсистемыКлиент.РазвернутьУзлыДерева(ЭтаФорма, "ИмяЭлементаДерево", "*", Истина);

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

Оптимизация и «хаки» для быстрого обновления

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

Способ 1: Манипуляция видимостью и начальным отображением. Посмотрим на интересный прием: если временно изменить свойство НачальноеОтображениеДерева и переключить видимость, платформа принудительно перерисует дерево в нужном состоянии:


&НаКлиенте
Процедура БыстроеРазвертывание()
    ЭлементДерево = Элементы.ДеревоДанных;
    ЭлементДерево.Видимость = Ложь;
    // Меняем режим отображения
    ЭлементДерево.НачальноеОтображениеДерева = НачальноеОтображениеДерева.РаскрыватьВсеУровни;
    
    // Хак: заставляем платформу думать, что это первичная отрисовка
    РазвернутьНаСервере(); 
    
    ЭлементДерево.Видимость = Истина;
КонецПроцедуры

&НаСервере
Процедура РазвернутьНаСервере()
    // Перезаполняем данные из реквизита в значение и обратно
    Дерево = РеквизитФормыВЗначение("ДеревоДанных");
    ЗначениеВРеквизитФормы(Дерево, "ДеревоДанных");
КонецПроцедуры

Способ 2: Использование обработчика ожидания. Проанализируем проблему из сообщения №46: при открытии формы методы свертки могут не отрабатывать, так как дерево еще не «отрисовано». В этом случае используйте ПодключитьОбработчикОжидания с минимальным интервалом (0.1 сек), чтобы выполнить код сразу после завершения инициализации формы.

Заключение и советы

Подведем итог и сформулируем основные рекомендации для работы с деревьями значений:

  1. Для обычных форм используйте метод Развернуть() непосредственно у табличного поля, передавая саму строку.
  2. Для управляемых форм всегда оперируйте идентификаторами строк через ПолучитьИдентификатор().
  3. Если нужно развернуть всё дерево в управляемой форме, используйте Элементы.ВашеДерево.Развернуть(0, Истина).
  4. Для корректной сворачиваемости всегда применяйте рекурсию «снизу вверх» (сначала дочерние, потом родители).
  5. Если в системе есть БСП, используйте СтандартныеПодсистемыКлиент.РазвернутьУзлыДерева — это сэкономит время и избавит от ошибок.
  6. Для ускорения работы на больших объемах данных используйте методы серверного перестроения данных с изменением НачальноеОтображениеДерева.

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

← На главную