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