Как правильно обработать закрытие произвольной формы 1С и выдать запрос на сохранение данных

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

При разработке пользовательских интерфейсов на платформе 1С:Предприятие 8.3 часто возникает необходимость организовать диалог подтверждения действия при закрытии формы. Например, когда пользователь ввел или изменил данные в произвольной форме и пытается закрыть ее — будь то нажатием кнопки «Отмена», клавиши Esc или крестика в правом верхнем углу окна.

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

В чем заключается проблема и почему возникает зацикливание

Разберем типичный сценарий. Разработчик хочет реализовать следующую логику:

  1. Нажатие на кнопку «ОК» («Да»): данные сохраняются, система вызывающим образом передает результат и закрывает форму.
  2. Нажатие на кнопку «Отмена»: если данные изменялись (флаг Модифицированность равен Истина), платформа переспрашивает пользователя: «Сохранить изменения?». В случае ответа «Да» данные сохраняются и передаются, в случае «Нет» — форма закрывается без сохранения.
  3. Закрытие окна через Esc или крестик: должно отрабатывать абсолютно так же, как и кнопка «Отмена».

Основная сложность кроется в обработке закрытия через крестик или Esc. У этих действий нет индивидуального события на форме. Единственное событие, которое перехватывает такое закрытие, — это клиентский обработчик ПередЗакрытием.

Если внутри процедуры ПередЗакрытием задать вопрос пользователю и затем программно вызвать метод Закрыть(), платформа повторно инициирует процесс закрытия формы и снова вызовет процедуру ПередЗакрытием. В результате приложение попадает в зацикливание, а вопрос о сохранении задается пользователю повторно.

Способ 1: Отслеживание состояния через флаг выбранного действия

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

Посмотрим на пример кода реализации данного подхода:


&НаКлиенте
Перем ВыбранноеДействие;

&НаКлиенте
Процедура OK(Команда)
    ВыбранноеДействие = "Да";
    ОповеститьОВыборе(Текст);
    Закрыть(Текст);
КонецПроцедуры

&НаКлиенте
Процедура Отмена(Команда)
    ВыбранноеДействие = "Отмена";
    ОтветНаВопрос = Вопрос("Сохранить изменения?", РежимДиалогаВопрос.ДаНет);
    Если ОтветНаВопрос = КодВозвратаДиалога.Да Тогда
        ВыбранноеДействие = "Да";
        ОповеститьОВыборе(Текст);
        Закрыть(Текст);
    Иначе
        ВыбранноеДействие = "Отмена";
        Закрыть(КодВозвратаДиалога.Отмена);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
    // Если закрытие вызвано нажатием на наши кнопки — логика уже отработала
    Если НЕ (ВыбранноеДействие = "Отмена" ИЛИ ВыбранноеДействие = "Да") Тогда
        // Пользователь нажал крестик или Esc
        ОтветНаВопрос = Вопрос("Сохранить изменения?", РежимДиалогаВопрос.ДаНет);
        Если ОтветНаВопрос = КодВозвратаДиалога.Да Тогда
            ВыбранноеДействие = "Да";
            ОповеститьОВыборе(Текст);
            Закрыть(Текст);
        Иначе
            ВыбранноеДействие = "Отмена";
            Закрыть(КодВозвратаДиалога.Отмена);
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

В этом примере переменная ВыбранноеДействие позволяет процедуре ПередЗакрытием определить, откуда начался процесс закрытия окна. Если закрытие инициировано пользователем через крестик или Esc, переменная пуста, и алгоритм задает вопрос.

Способ 2: Современный асинхронный двухпроходный шаблон (Рекомендуемый)

В современных конфигурациях 1С включен режим запрета модальных окон. Использование синхронной функции Вопрос() недопустимо, так как это приведет к ошибке при работе в веб-клиенте. Задавать вопросы необходимо асинхронно с использованием процедуры ПоказатьВопрос() и объекта ОписаниеОповещения.

Однако сделать сам обработчик ПередЗакрытием асинхронным нельзя: платформе требуется мгновенное значение параметра Отказ. Если не отменить закрытие сразу, форма закроется до того, как пользователь успеет ответить на вопрос.

Для решения этой проблемы применяют двухпроходный шаблон закрытия:

  1. Первый проход: В процедуре ПередЗакрытием проверяем флаг. Если вопрос еще не задавался и форма модифицирована, устанавливаем Отказ = Истина, тем самым блокируя закрытие формы, и открываем асинхронный диалог ПоказатьВопрос().
  2. Обработка ответа: Пользователь выбирает вариант в диалоговом окне. В процедуре оповещения мы взводим флаг, сохраняем данные (при необходимости) и повторно вызываем метод ЭтаФорма.Закрыть().
  3. Второй проход: При повторном вызове снова срабатывает ПередЗакрытием, однако флаг уже установлен. Процедура завершается без установки Отказ = Истина, и платформа успешно закрывает форму.

Разберем реализацию этого шаблона на практике:


&НаКлиенте
Перем ОтветНаВопросЗадан; // Переменная модуля формы

&НаКлиенте
Процедура OK(Команда)
    ОтветНаВопросЗадан = Истина;
    ОповеститьОВыборе(Текст);
    Закрыть(Текст);
КонецПроцедуры

&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
    
    // 1. Если вопрос уже был обработан — разрешаем штатное закрытие
    Если ОтветНаВопросЗадан = Истина Тогда
        Возврат;
    КонецЕсли;

    // 2. Если данные не менялись, переспрашивать не нужно
    Если НЕ Модифицированность Тогда
        Возврат;
    КонецЕсли;

    // 3. Блокируем закрытие формы на время ответа пользователя
    Отказ = Истина;

    // 4. Задаем асинхронный вопрос
    Оповещение = Новый ОписаниеОповещения("ПослеОтветаНаВопросСохранения", ЭтотОбъект);
    ПоказатьВопрос(Оповещение, "Вы хотите сохранить изменения?", РежимДиалогаВопрос.ДаНет);

КонецПроцедуры

&НаКлиенте
Процедура ПослеОтветаНаВопросСохранения(Результат, ДополнительныеПараметры) Экспорт
    
    // Фиксируем, что ответ от пользователя получен
    ОтветНаВопросЗадан = Истина;

    Если Результат = КодВозвратаДиалога.Да Тогда
        ОповеститьОВыборе(Текст);
        Закрыть(Текст);
    Иначе
        Закрыть(КодВозвратаДиалога.Отмена);
    КонецЕсли;

КонецПроцедуры

Подведение итогов

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

← На главную