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