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