Как правильно позиционировать курсор в дереве значений после обновления данных

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

Работа с иерархическими структурами в 1С, такими как ДеревоЗначений, на управляемых формах часто преподносит сюрпризы. Одна из самых распространенных проблем — это «слет» фокуса или некорректное выделение строки после того, как дерево было программно пересчитано или обновлено на сервере. В этой статье мы подробно разберем, почему визуальный курсор может оставаться на первой строке, в то время как программно активной считается другая, и как добиться стабильного поведения интерфейса.

Почему сбивается позиционирование?

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

Если после этого мы пытаемся установить ТекущаяСтрока, используя старый индекс или идентификатор, платформа нас «не поймет». Даже если мы найдем новый идентификатор (например, с помощью БСП), часто возникает эффект «двойного выделения»: одна строка подсвечена как выбранная, но рамка фокуса и прокрутка экрана остаются на самой первой позиции дерева. Выясним причину этого поведения и рассмотрим шаги для исправления.

Шаг 1. Поиск актуального идентификатора строки

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


// Предположим, у нас есть УникальныйИдентификатор строки, которую редактировали
ЗначениеВРеквизитФормы(ДеревоЗначений, "Дерево");

// Ищем новый ID строки в обновленном дереве
ПараметрыПоиска = Новый Структура("КлючСвязи", СохраненныйКлюч);
ИдентификаторСтроки = ОбщегоНазначенияКлиентСервер.ПолучитьИдентификаторСтрокиДереваПоЗначениюПоля(
    ЭтаФорма.Дерево, 
    "КлючСвязи", 
    СохраненныйКлюч
);

Шаг 2. Развертывание иерархии

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


// Рекурсивно или последовательно разворачиваем дерево до нужного узла
Элементы.Дерево.Развернуть(ИдентификаторСтроки, Истина);

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

Шаг 3. Правильная последовательность установки фокуса

Одной из главных причин неудачи является неверный порядок установки свойств ТекущаяСтрока и ТекущийЭлемент. Многие разработчики пытаются сначала установить фокус на таблицу, а потом присвоить строку, или наоборот, но делают это не совсем точно. Рассмотрим наиболее эффективную комбинацию.

Важный секрет: устанавливать ТекущийЭлемент нужно не на саму таблицу (дерево), а на конкретное поле (колонку) внутри этой таблицы. Это заставляет платформу жестко привязать фокус ввода к конкретной ячейке.


// 1. Указываем активную строку
Элементы.Дерево.ТекущаяСтрока = ИдентификаторСтроки;

// 2. Устанавливаем фокус на КОЛОНКУ дерева, а не на само дерево
ЭтаФорма.ТекущийЭлемент = Элементы.ДеревоНаименование; 

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

Шаг 4. Использование обработчика ожидания (борьба с асинхронностью)

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

Разберем по шагам, как это реализовать:

  1. В основной процедуре (например, ПослеЗаписи или ПриИзменении) сохраняем нужный ID во временную переменную модуля формы.
  2. Подключаем обработчик с минимальным интервалом (0.1 сек).
  3. В процедуре обработчика выполняем позиционирование.

// В коде формы
&НаКлиенте
Перем СохраненныйИД;

&НаКлиенте
Процедура ПослеОбновленияДанных()
    // ... логика обновления ...
    СохраненныйИД = НайтиНовыйИдентификатор();
    ПодключитьОбработчикОжидания("УстановитьФокусНаСтроку", 0.1, Истина);
КонецПроцедуры

&НаКлиенте
Процедура УстановитьФокусНаСтроку()
    Если СохраненныйИД <> Неопределено Тогда
        Элементы.Дерево.ТекущаяСтрока = СохраненныйИД;
        ЭтаФорма.ТекущийЭлемент = Элементы.ДеревоНаименование;
    КонецЕсли;
КонецПроцедуры

Шаг 5. Очистка коллекции выделенных строк

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


Элементы.Дерево.ВыделенныеСтроки.Очистка();
Элементы.Дерево.ТекущаяСтрока = ИдентификаторСтроки;

Альтернативный подход: Обновление без полной перезагрузки

Проанализируем ситуацию: стоит ли вообще использовать ЗначениеВРеквизитФормы? Если дерево очень большое, полная перезагрузка не только сбивает фокус, но и замедляет работу системы. Рассмотрим вариант ручного обновления дерева.

Вместо полной замены объекта можно обойти коллекцию Дерево.ПолучитьЭлементы() и обновить значения только в измененных строках. В этом случае идентификаторы строк сохраняются, и вам вообще не придется заботиться о восстановлении позиции курсора — платформа сделает это автоматически, так как объекты строк не пересоздавались.

Резюме решения

Для стабильного позиционирования в дереве значений рекомендуем придерживаться следующего алгоритма:

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

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

← На главную