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