Почему сворачивается дерево значений при изменении флажка и как это исправить

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

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

Выясняем причину: почему дерево «схлопывается»?

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

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

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

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

Вариант 1. Использование обработчика «ПередСворачиванием» (быстрый, но несовершенный способ)

На форумах часто встречается решение, основанное на программном запрете сворачивания в момент изменения данных. Давайте проанализируем ситуацию и посмотрим на пример кода, который часто предлагают новички:

Суть метода заключается в создании контекстной переменной модуля формы (на клиенте), которая служит флагом-предохранителем.


&НаКлиенте
Перем ОтказОтСворачивания;

&НаКлиенте
Процедура СписокПриИзмененииФлажка(Элемент)
    // Устанавливаем флаг перед тем, как данные уйдут на сервер
    ОтказОтСворачивания = Истина;
    // Логика изменения данных...
КонецПроцедуры

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

Почему этот способ — «костыль»? Рассмотрим последствия. Главная проблема заключается в том, что этот метод ломает штатное поведение интерфейса. Пользователи замечают, что для того чтобы действительно свернуть узел дерева вручную, им приходится нажимать на значок «минус» дважды. Первый клик лишь сбрасывает программный флаг, и только второй выполняет действие. Это путает пользователя и делает интерфейс «неотзывчивым».

Вариант 2. Профессиональный подход: сохранение и восстановление состояния

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

Нам потребуется вспомогательная функция, которая будет обходить дерево и собирать ID всех развернутых узлов в массив. Проанализируем пример реализации:

Шаг 1. Получение списка развернутых узлов


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

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

Шаг 2. Восстановление состояния после сервера

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


&НаКлиенте
Процедура ВосстановитьРазвернутость(МассивID, ИмяЭлементаДерева)
    Для каждого Идентификатор Из МассивID Цикл
        Элементы[ИмяЭлементаДерева].Развернуть(Идентификатор);
    КонецЦикла;
КонецПроцедуры

Важно: Идентификаторы строк (RowID) в управляемых формах обычно сохраняются в течение жизни формы, если вы не пересоздаете таблицу целиком на сервере. Если же происходит полная перезагрузка данных из БД, идентификаторы могут измениться. В этом случае вместо ID следует сохранять уникальные ключи самих данных (например, Ссылка или уникальный код строки).

Вариант 3. Оптимизация клиент-серверного взаимодействия

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

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

Дополнительные рекомендации

Рассмотрим еще несколько приемов, которые помогут сделать работу с деревом более стабильной:

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

← На главную