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