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