В процессе разработки на платформе 1С:Предприятие 8.3 часто возникает задача: понять, какое именно действие инициировал пользователь — просто сохранение данных или сохранение с последующим закрытием формы. Стандартные события формы, такие как ПередЗаписью или ПриЗаписи, не содержат в своих параметрах явного флага, указывающего на источник вызова. Ситуация осложняется, когда в код внедряются асинхронные вызовы (например, вопросы пользователю), которые прерывают стандартную цепочку событий платформы. Рассмотрим подробнее, почему это происходит и как элегантно решить данную проблему. Для лучшего понимания работы с неблокирующими вызовами в платформе можно изучить методику применения асинхронных методов.
Разберем стандартное поведение управляемой формы. Когда пользователь нажимает «Записать и закрыть», платформа выполняет скрытую последовательность действий: сначала вызывается метод Записать(), и в случае его успеха вызывается метод Закрыть(). Если в процедуре ПередЗаписью мы устанавливаем Отказ = Истина (например, чтобы показать пользователю ПоказатьВопрос и дождаться ответа), выполнение прерывается. Когда после получения ответа мы вызываем ЭтаФорма.Записать() программно, платформа воспринимает это как новую, самостоятельную команду записи. Информация о том, что изначально планировалось закрытие формы, теряется. Выясним причину: контекст вызова команды не сохраняется между вызовами процедур, если только мы не передадим его принудительно.
Наиболее надежный и «чистый» способ — перехватить действие стандартной кнопки и внедрить в структуру параметров записи свой собственный признак. Проанализируем реализацию этого метода по шагам.
Шаг 1. Перехват команды при создании формы. Нам не нужно удалять стандартные кнопки. Мы можем программно изменить обработчик (действие) существующей команды в процедуре ПриСозданииНаСервере. Посмотрим на пример кода:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Определяем имя стандартной команды для записи и закрытия
ИмяКомандыЗаписатьИЗакрыть = "ЗаписатьИЗакрыть";
// Создаем свою команду, которая будет выполнять роль посредника
НоваяКоманда = ЭтаФорма.Команды.Добавить("Подключаемый_ЗаписатьИЗакрыть");
НоваяКоманда.Действие = "Подключаемый_ЗаписатьИЗакрыть";
НоваяКоманда.Заголовок = "Записать и закрыть";
// Перенаправляем кнопку на нашу новую команду
// Обычно кнопка называется "ФормаЗаписатьИЗакрыть"
ЭлементКнопка = ЭтаФорма.КоманднаяПанель.ПодчиненныеЭлементы.Найти("ФормаЗаписатьИЗакрыть");
Если ЭлементКнопка <> Неопределено Тогда
ЭлементКнопка.ИмяКоманды = "Подключаемый_ЗаписатьИЗакрыть";
КонецЕсли;
КонецПроцедуры
Шаг 2. Реализация обработчика на клиенте. Теперь создадим процедуру, которая будет вызываться при нажатии кнопки. Вместо стандартного вызова мы будем использовать метод Записать(), передавая в него структуру ПараметрыЗаписи с нашим флагом.
&НаКлиенте
Процедура Подключаемый_ЗаписатьИЗакрыть(Команда)
// Формируем структуру параметров записи
ДопПараметры = Новый Структура;
ДопПараметры.Вставить("ЗакрытьПослеЗаписи", Истина);
// Инициируем запись с нашими параметрами
ЭтаФорма.Записать(ДопПараметры);
КонецПроцедуры
Шаг 3. Обработка в событии «ПередЗаписью». Теперь в стандартном событии ПередЗаписью мы можем проверить наличие нашего ключа в структуре ПараметрыЗаписи. Рассмотрим ситуацию, когда нам нужно задать вопрос пользователю:
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Проверяем, не является ли это повторным вызовом после вопроса
Если ПараметрыЗаписи.Свойство("ПродолжениеПослеВопроса") Тогда
Возврат;
КонецЕсли;
// Если нам нужно прервать запись для вопроса
Отказ = Истина;
// Сохраняем информацию о необходимости закрытия
НужноЗакрыть = Ложь;
ПараметрыЗаписи.Свойство("ЗакрытьПослеЗаписи", НужноЗакрыть);
// Готовим параметры для оповещения
Контекст = Новый Структура;
Контекст.Вставить("ЗакрытьПослеЗаписи", НужноЗакрыть);
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеОтветаНаВопрос", ЭтотОбъект, Контекст);
ПоказатьВопрос(ОписаниеОповещения, "Данные будут изменены. Продолжить?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
После того как пользователь ответил «Да», нам нужно повторно вызвать запись и, если флаг ЗакрытьПослеЗаписи установлен, закрыть форму. Проанализируем код завершающего этапа:
&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, Контекст) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
// Создаем параметры для повторного вызова записи
НовыеПараметры = Новый Структура;
НовыеПараметры.Вставить("ПродолжениеПослеВопроса", Истина);
// Передаем признак закрытия дальше
Если Контекст.ЗакрытьПослеЗаписи Тогда
НовыеПараметры.Вставить("ЗакрытьПослеЗаписи", Истина);
КонецЕсли;
// Выполняем запись. Если все ок, сработает ПослеЗаписи
Если ЭтаФорма.Записать(НовыеПараметры) Тогда
// Если запись прошла успешно и флаг стоял - закрываем
Если Контекст.ЗакрытьПослеЗаписи Тогда
ЭтаФорма.Закрыть();
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Стоит отметить важный нюанс, упомянутый в обсуждении: если вы используете расширения, работа с процедурой ПередЗаписью должна быть аккуратной. Проверяйте Отказ в самом начале, чтобы не переопределять логику других расширений или типовой конфигурации. Также рекомендуется проверять успешность записи перед вызовом Закрыть(), чтобы не закрыть форму, если запись не удалась из-за проверок на сервере.
Рассмотрим альтернативный, менее трудоемкий способ управления закрытием. У управляемой формы существует стандартное свойство ЗакрыватьПриЗаписи (CloseOnWrite). Если установить его в значение Истина, любой успешный вызов метода Записать() (включая программный) приведет к автоматическому закрытию формы платформой.
Посмотрим, как это упрощает логику:
ЭтаФорма.ЗакрыватьПриЗаписи = Истина; и вызываем ЭтаФорма.Записать();.ЭтаФорма.ЗакрыватьПриЗаписи = Ложь;.Этот метод избавляет от необходимости вручную пробрасывать флаги через структуру ПараметрыЗаписи во всех асинхронных процедурах, так как свойство формы сохраняет свое состояние до завершения операции.
Проанализировав ситуацию, мы пришли к выводу, что штатных средств для прямого определения кнопки в событии ПередЗаписью не существует. Однако комбинация переопределения действия команды и использования структуры ПараметрыЗаписи позволяет полностью контролировать процесс. При работе с асинхронными диалогами это единственный способ сохранить намерение пользователя закрыть форму. Помните о важности обработки Отказ и проверке модифицированности объекта, чтобы обеспечить корректный UX и целостность данных в вашей конфигурации. При необходимости реализации сложной динамической модификации интерфейса можно использовать готовый модуль для программного изменения управляемых форм.