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

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

В разработке на платформе 1С:Предприятие 8.3 (Управляемые формы) событие ПриАктивизацииСтроки является одним из самых часто используемых и, одновременно, самых коварных. Программисты часто сталкиваются с ситуацией, когда код внутри этого обработчика начинает выполняться «сам по себе», даже если пользователь не кликал по таблице. Это приводит к замедлению работы интерфейса, лишним серверным вызовам или некорректному обновлению данных на форме.

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

Выясняем причину: почему платформа инициирует событие?

Проанализируем ситуацию, описанную автором темы. В его коде в процедуре СуммаВПодвалеНаКлиенте происходит активное изменение свойства ТекстПодвала для множества колонок. Кроме того, в процедуре СписокЭКР устанавливается ОтборСтрок. Рассмотрим, как на это реагирует управляемая форма:

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

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

Решение 1: Использование флага блокировки (программная защелка)

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

Сначала объявим переменную в модуле формы:


&НаКлиенте
Переменная ОбновлениеИдет;

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


&НаКлиенте
Процедура ФТЧФКРПриАктивизацииСтроки(Элемент)
    
    // Если флаг установлен, просто выходим из процедуры
    Если ОбновлениеИдет = Истина Тогда
        Возврат;
    КонецЕсли;

    Если Элемент.ТекущиеДанные = НеОпределено Тогда
        Возврат;    
    КонецЕсли;        
    
    Объект.ИтогВПодвалеПР = Элементы.ФТЧФКР.ТекущиеДанные.ФКР;
    СписокЭКР(Ложь);
        
КонецПроцедуры

&НаКлиенте
Процедура СброситьФильтр(Команда)
    
    // Устанавливаем блокировку перед началом программных изменений
    ОбновлениеИдет = Истина;
    
    Объект.ИтогВПодвалеПР  = "";
    Объект.Организация     = "";
    СписокЭКР(Истина);
    
    // Снимаем блокировку после завершения всех манипуляций
    ОбновлениеИдет = Ложь;
    
КонецПроцедуры

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

Решение 2: Проверка изменения текущей строки (Идентификатор)

Иногда событие вызывается для одной и той же строки несколько раз подряд. Чтобы избежать лишних расчетов, мы можем запоминать идентификатор строки, которую уже обработали. Посмотрим на пример кода:


&НаКлиенте
Переменная ПоследнийИдентификатор;

&НаКлиенте
Процедура ФТЧФКРПриАктивизацииСтроки(Элемент)
    
    ТекущаяСтрока = Элемент.ТекущаяСтрока;
    
    // Проверяем, не та ли это строка, которую мы обрабатывали мгновение назад
    Если ТекущаяСтрока = ПоследнийИдентификатор Тогда
        Возврат;
    КонецЕсли;
    
    ПоследнийИдентификатор = ТекущаяСтрока;
    
    // Далее следует ваша логика...
    СписокЭКР(Ложь);
    
КонецПроцедуры

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

Решение 3: Перенос логики в событие Выбор (OnChoice)

Автор темы в итоге пришел к решению использовать событие Выбор (двойной клик или Enter). Это радикальный, но эффективный способ, если бизнес-логика позволяет обновлять данные не просто при перемещении курсора, а именно при активном выборе строки пользователем.

Рассмотрим пример реализации:


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

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

Решение 4: Использование обработчика ожидания

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

Алгоритм действий следующий:

  1. В событии ПриАктивизацииСтроки мы только даем команду на запуск расчета.
  2. Сам расчет выполняется отдельной процедурой через 0.1 - 0.2 секунды.

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

&НаКлиенте
Процедура ВыполнитьРасчетИтогов()
    // Здесь основной код расчета и фильтрации
    СписокЭКР(Ложь);
КонецПроцедуры

Рекомендации по оптимизации кода подвала

Проанализируем процедуру СуммаВПодвале. Автор использует Объект.ТЧРасходыПоПлатежам.Выгрузить() на сервере. Если в таблице тысячи строк, то каждое перемещение по строкам будет вызывать:

Совет: Вместо выгрузки всей ТЧ, попробуйте использовать метод Итог("ИмяКолонки"), если вам нужны общие итоги, или используйте Запрос к данным объекта, если нужны сложные фильтры. Это значительно ускорит работу и снизит нагрузку на сервер.

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

← На главную