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