Как передать параметры при закрытии управляемой формы в 1С (включая закрытие по кнопке «Крестик»)?

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

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

Попытки прописать передачу параметров напрямую в событиях закрытия формы часто приводят либо к ошибке «Невозможно выполнить команду в закрытой форме», либо к бесконечному зацикливанию приложения. В этой статье мы подробно разберем, почему возникают эти ошибки, и рассмотрим несколько надежных способов корректной передачи параметров при любом сценарии закрытия формы.

Почему стандартные попытки вызывают ошибки?

Для начала давайте проанализируем причины возникновения ошибок, с которыми сталкиваются разработчики при попытке перехватить закрытие формы «крестиком».

Ситуация 1. Вызов метода Закрыть() в обработчике ПриЗакрытии()

Рассмотрим следующий код:


&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
    // Попытка передать параметр при закрытии формы
    ЭтаФорма.Закрыть(ПередаваемыйПараметр); 
КонецПроцедуры

Этот код гарантированно вызовет ошибку платформы. Дело в том, что событие ПриЗакрытии срабатывает тогда, когда процесс закрытия формы уже запущен и фактически завершен. Форма уже закрыта, ее визуальная часть уничтожена, и вызывать метод Закрыть() повторно запрещено на уровне ядра платформы.

Ситуация 2. Вызов метода Закрыть() в обработчике ПередЗакрытием()

Пытаясь обойти предыдущую проблему, разработчики часто пишут такой код:


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

Такой подход приводит к бесконечной рекурсии (зацикливанию). Метод Закрыть() инициализирует процедуру закрытия формы, которая первым делом снова вызывает обработчик ПередЗакрытием. В нем опять срабатывает код Отказ = Истина и снова вызывается Закрыть(). Платформа уходит в бесконечный цикл, пока не произойдет переполнение стека вызовов.


Способ 1. Использование переменной-флага в событии ПередЗакрытием (Рекомендуемый способ)

Чтобы изящно обойти проблему зацикливания, мы можем использовать специальную клиентскую переменную модуля формы, которая будет выступать в роли флага. Мы разрешим закрытие формы только тогда, когда оно инициировано нашим программным кодом.

Давайте разберем этот алгоритм по шагам:

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

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


&НаКлиенте
Перем ЗакрытиеРазрешено; // Клиентская переменная модуля формы

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

Этот способ является универсальным стандартом для управляемого приложения при работе в немодальном режиме.


Способ 2. Стандартный подход платформы — Обработка «Неопределено»

С точки зрения интерфейсных стандартов фирмы «1С», нажатие пользователем на кнопку «крестик» или клавишу Esc трактуется как отказ от выбора или отмена операции. В этом случае вызывающая форма должна получить результат Неопределено.

Если мы следуем этому стандарту, нам не нужно перехватывать событие закрытия в самой закрывающейся форме. Достаточно правильно обработать результат в вызывающей форме. Рассмотрим, как это выглядит на практике при открытии формы в современном немодальном режиме с использованием описания оповещения.

Код в вызывающей форме (откуда открываем окно):


&НаКлиенте
Процедура ОткрытьФормуВвода(Команда)
    
    // Формируем описание оповещения, которое сработает после закрытия формы
    ОповещениеОЗакрытии = Новый ОписаниеОповещения("ПослеЗакрытияФормыВвода", ЭтотОбъект);
    
    // Открываем форму немодально, блокируя окно владельца
    ОткрытьФорму("ОбщаяФорма.ФормаВводаПараметров", , , , , , ОповещениеОЗакрытии, РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
    
КонецПроцедуры

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

В закрывающейся форме на кнопке «ОК» мы пишем простой вызов: Закрыть(ЗначениеПараметра), а кнопку «Отмена» привязываем к стандартной команде закрытия формы (которая вернет Неопределено).


Способ 3. Передача данных через ОповеститьОВыборе()

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

Рассмотрим пример реализации:


&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
    Если ЗавершениеРаботы Тогда
        Возврат;
    КонецЕсли;
    
    // Отправляем выбранное значение владельцу формы перед ее уничтожением
    ОповеститьОВыборе(Объект.ВыбранноеЗначение);
КонецПроцедуры

В вызывающей форме (у которой открываемая форма была подчиненной или назначена владельцем) автоматически сработает стандартный обработчик события ОбработкаВыбора:


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

Способ 4. Использование глобальных оповещений

Если формы открываются независимо друг от друга и между ними нет прямой связи «Владелец-Подчиненный», лучшим решением будет использование механизма глобальных оповещений платформы. Метод Оповестить() посылает сигнал всем открытым формам клиентского сеанса.

В закрывающейся форме мы прописываем отправку оповещения при закрытии:


&НаКлиенте
Процедура ПриЗакрытии(ЗавершениеРаботы)
    Если ЗавершениеРаботы Тогда
        Возврат;
    КонецЕсли;
    
    // Посылаем широковещательное сообщение в систему
    Оповестить("ЗаписаныПараметрыИнтеграции", Объект.ТекущиеПараметры, ЭтотОбъект);
КонецПроцедуры

В вызывающей (или любой другой заинтересованной) форме нам достаточно настроить предопределенный обработчик ОбработкаОповещения:


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    // Проверяем, наше ли это событие
    Если ИмяСобытия = "ЗаписаныПараметрыИнтеграции" Тогда
        // Обрабатываем переданную структуру параметров
        ОбновитьИнтерфейсНаОсновеПараметров(Параметр);
    КонецЕсли;
КонецПроцедуры

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


Важное замечание по поводу модальности в 1С 8.3

В исходном обсуждении на форуме упоминается открытие формы в модальном режиме (ОткрытьФормуМодально()). Настоятельно рекомендуем отказаться от использования модальности в современных конфигурациях 1С.

Использование модальных окон запрещено стандартами разработки «1С» по следующим причинам:

Вместо модального открытия всегда используйте свойство формы РежимОткрытияОкнаФормы.БлокироватьОкноВладельца совместно с объектом ОписаниеОповещения, как показано во втором способе данной статьи. Это обеспечит стабильную работу вашей конфигурации на любых платформах, включая мобильные клиенты и веб-интерфейсы. Для отказа от модальности при работе с файлами можно использовать методику асинхронного выбора и чтения файлов.

← На главную