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