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