Почему сбрасывается курсор при активизации строки на управляемой форме 1С и как это исправить?

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

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

Причины сброса фокуса на первую строку

Проанализируем, почему платформа ведет себя подобным образом. В справке к событию ПриАктивизацииСтроки элемента ТаблицаФормы содержится строгое системное ограничение:

«В обработчике данного события нельзя использовать серверные методы формы с директивой компиляции &НаСервере».

Выясним внутреннюю механику этой ошибки:

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

Способ 1. Использование неконтекстного вызова &НаСервереБезКонтекста

Самое быстрое и прямое решение проблемы — исключить передачу контекста формы на сервер. Для этого вынесем серверный алгоритм выборки данных в процедуру или функцию с директивой &НаСервереБезКонтекста.

Разберем по шагам правильную реализацию:

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

Посмотрим на пример кода:


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

&НаСервереБезКонтекста
Функция ПолучитьДанныеПодчиненнойТаблицыНаСервере(СсылкаНаОбъект)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ТЧ.Номенклатура КАК Номенклатура,
        |   ТЧ.Количество КАК Количество
        |ИЗ
        |   Документ.ЗаказКлиента.Товары КАК ТЧ
        |ГДЕ
        |   ТЧ.Ссылка = &СсылкаНаОбъект";
    Запрос.УстановитьПараметр("СсылкаНаОбъект", СсылкаНаОбъект);
    
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Способ 2. Подключение обработчика ожидания (паттерн Debounce)

Если пользователь быстро перемещается по строкам списка с помощью клавиатуры (стрелками вверх и вниз), событие ПриАктивизацииСтроки будет срабатывать на каждой промежуточной строке. Это создает ненужную пиковую нагрузку на серверную базу данных. Чтобы этого избежать, применим обработчик ожидания с небольшой задержкой (0.1–0.3 секунды).

Рассмотрим реализацию механизма отложенного обновления:


&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    // Отключаем предыдущий вызов, если пользователь еще двигается по списку
    ОтключитьОбработчикОжидания("ВыполнитьОбновлениеПодчиненнойТаблицы");
    
    // Подключаем однократный обработчик с задержкой в 0.2 секунды
    ПодключитьОбработчикОжидания("ВыполнитьОбновлениеПодчиненнойТаблицы", 0.2, Истина);
КонецПроцедуры

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

Способ 3. Замена таблицы значений на второй Динамический список

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

Разберем алгоритм настройки:

  1. В запрос второго динамического списка добавляем условие с параметром: ГДЕ Ссылка = &ТекущийВладелец.
  2. В обработчике ПриАктивизацииСтроки основного списка устанавливаем параметр подчиненного динамического списка без вызова сервера:

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Установка параметра динамического списка выполняется полностью на клиенте
    СписокПодчиненный.Параметры.УстановитьЗначениеПараметра(
        "ТекущийВладелец", 
        ТекущиеДанные.Ссылка
    );
КонецПроцедуры

Такой подход не сбрасывает фокус, исключает лишний клиент-серверный код и обеспечивает автоматическую порционную подгрузку данных платформой 1С.

← На главную