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

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

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

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

Выясним причину: почему сбивается позиционирование

Проанализируем основные факторы, приводящие к потере текущей строки и ячейки:

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

Способ 1. Отказ от перезагрузки дерева и работа с коллекцией формы напрямую

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

Посмотрим на пример обработки события при изменении ячейки:


&НаКлиенте
Процедура ДеревоКолонкаСуммаПриИзменении(Элемент)
    
    // Запоминаем текущую строку и текущую колонку
    ТекущийИдентификатор = Элементы.Дерево.ТекущаяСтрока;
    ТекущаяКолонка = Элементы.Дерево.ТекущаяКолонка;
    
    // Выполняем пересчет на сервере без перезаполнения дерева целиком
    ПересчитатьДанныеНаСервере(ТекущийИдентификатор);
    
    // Восстанавливаем позиционирование
    Элементы.Дерево.ТекущаяСтрока = ТекущийИдентификатор;
    Элементы.Дерево.ТекущаяКолонка = ТекущаяКолонка;
    
    // Возвращаем фокус ввода в нужную колонку дерева
    ЭтаФорма.ТекущийЭлемент = ТекущаяКолонка;
    
КонецПроцедуры

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

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

Способ 2. Выполнение расчетов на стороне клиента

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

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


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

Способ 3. Позиционирование при вынужденном пересоздании дерева

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

  1. Добавим в структуру реквизита дерева скрытую колонку КлючСтроки (строковый GUID или уникальный код).
  2. Перед серверным вызовом запомним ключ текущей строки:
    
    ТекущиеДанные = Элементы.Дерево.ТекущиеДанные;
    КлючАктивнойСтроки = ТекущиеДанные.КлючСтроки;
    КолонкаДляФокуса = Элементы.Дерево.ТекущаяКолонка;
    
  3. После выполнения ЗначениеВРеквизитФормы() выполним поиск строки по сохраненному ключу, обратившись к элементам дерева через метод ПолучитьЭлементы().
  4. У найденного элемента вызовем метод ПолучитьИдентификатор() и передадим новое значение в Элементы.Дерево.ТекущаяСтрока.
  5. Завершим восстановление фокуса установкой свойств Элементы.Дерево.ТекущаяКолонка = КолонкаДляФокуса; и ЭтаФорма.ТекущийЭлемент = КолонкаДляФокуса;.

Рекомендации для гарантированного перехода в режим редактирования

Если после возврата курсора в ячейку требуется сразу перевести ее в режим ввода текста (без необходимости повторного двойного клика мыши или нажатия клавиши Enter), применим метод таблицы ИзменитьСтроку():


Элементы.Дерево.ТекущаяСтрока = ТекущийИдентификатор;
Элементы.Дерево.ТекущаяКолонка = Элементы.ДеревоКолонкаСумма;
ЭтаФорма.ТекущийЭлемент = Элементы.Дерево;
Элементы.Дерево.ИзменитьСтроку();

Если ветка дерева с активной строкой могла свернуться при обновлении, перед установкой текущей строки развернем родительский узел с помощью метода Элементы.Дерево.Развернуть(ИдентификаторРодителя, Истина);. В таком комплексе настроек интерфейс сохраняет полную стабильность при любых серверных пересчетах.

← На главную