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