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

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

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

Почему сбрасывается развернутость строк и не обновляются данные

Проанализируем типичные ошибки при попытке управлять состоянием дерева через объектный контекст:

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

Выясним причину главного противоречия: если не вызывать ЗначениеВРеквизитФормы, изменения серверного объекта не попадут на форму, а если вызвать — визуальное дерево схлопнется. Выход заключается в том, чтобы работать непосредственно с данными формы на клиенте, минуя тяжелые серверные вызовы и конвертацию структуры.

Решение 1. Изменение признака развернутости напрямую в данных формы

Рассмотрим, как зафиксировать разворачивание узла без обращения к серверному объекту. Если у реквизита дерева на форме есть служебная колонка Развернут, мы можем обращаться к текущей строке на клиенте через коллекцию ДанныеФормыДерево по идентификатору.

Посмотрим на пример обработчиков событий таблицы дерева на форме:


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

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

Такой подход не требует вызова сервера, мгновенно отрабатывает в интерфейсе и гарантирует, что признак развернутости останется в реквизите формы без сброса интерфейсного состояния таблицы.

Решение 2. Надежное сохранение и восстановление состояния дерева между сеансами

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

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

Рассмотрим пример обхода и разворачивания строк на клиенте:


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

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

Для программного управления визуальным состоянием веток используются специализированные методы таблицы формы: Элементы.ИмяЭлемента.Развернуть() и Элементы.ИмяЭлемента.Свернуть(). Они принимают именно числовой идентификатор элемента данных формы, полученный через вызов метода ПолучитьИдентификатор().

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

← На главную