Как запустить событие формы, объекта или подписки из кода 1С

· 9 мин

Содержание

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

Метода НайтиОбработчикСобытия, который иногда советуют в интернете, в платформе 1С:Предприятие 8.3 нет. Проверьте сами: откройте синтакс-помощник в Конфигураторе и найдите это имя. Всё, что нужно для программного вызова, собрано в четырёх механизмах: прямой вызов процедуры, описание оповещения, оповещение форм и запись объекта.

Откуда берутся события и кто их вызывает

В 1С события бывают трёх видов, и способ запуска у каждого свой.

  • 🧩 События формы и её элементов — ПриОткрытии, ПриИзменении, НачалоВыбора. Обработчик лежит в модуле формы.
  • 📄 События объекта — ПередЗаписью, ПриЗаписи, ОбработкаПроведения. Обработчик лежит в модуле объекта или менеджера.
  • 🔔 Подписки на события — процедуры общего модуля, которые срабатывают на события объектов независимо от формы.

Различать их нужно по одной причине: у формы и у объекта обработчик можно вызвать по имени, а подписку — только через действие, которое порождает событие. Подписка привязана к событию объекта, а не к имени процедуры.

Отдельно живут события оповещения: форма вызывает Оповестить, а все открытые формы получают ОбработкаОповещения. Это внутренний канал связи между окнами, и он тоже запускается из кода.

Прямой вызов процедуры-обработчика

Самый простой путь. Обработчик события — процедура в модуле, и вы можете позвать её как любую другую.

В модуле той же формы:

&НаКлиенте
Процедура ПриИзмененииКонтрагента(Элемент)
    // пересчёт зависимых полей
КонецПроцедуры

&НаКлиенте
Процедура ЗаполнитьИзКода()
    Объект.Контрагент = ВыбранныйКонтрагент;
    ПриИзмененииКонтрагента(Элементы.Контрагент);
КонецПроцедуры

Смысл здесь такой. Когда вы меняете значение реквизита из кода, событие ПриИзменении не срабатывает. Оно запускается только при правке пользователем. Поэтому после программного присвоения обработчик зовут вручную.

Если вызывать обработчик нужно из другого модуля, процедуру делают экспортной:

&НаКлиенте
Процедура ПриИзмененииКонтрагента(Элемент) Экспорт

Тогда другая форма, получившая ссылку на эту (например, через ВладелецФормы), может написать ВладелецФормы.ПриИзмененииКонтрагента(Неопределено). Параметр Элемент обработчик часто не использует, поэтому передают Неопределено. Но если внутри читается Элемент.Имя, передавайте реальный элемент.

Не копируйте тело обработчика в три места. Вынесите расчёт в отдельную процедуру, а обработчик сделайте тонкой обёрткой. Тогда и событие, и ваш код вызывают одно и то же.

Что не получится вызвать напрямую

Процедуры формы с директивой &НаСервере вызываются с клиента, но не наоборот. Клиентская процедура с серверной стороны недоступна. Если вам нужно «запустить обработчик формы» в фоновом задании или регламентном задании, формы там просто нет. Выносите логику в общий модуль.

Обработчики объекта тоже не зовут «снаружи» без нужды. Вызов Документ.ПередЗаписью(Отказ) из чужого модуля не пройдёт: эти процедуры не экспортные. Для запуска событий объекта есть штатный путь, он описан ниже.

ОписаниеОповещения: обработчик как значение

Когда обработчик должен сработать позже (после закрытия формы, ответа на вопрос, выбора файла), в 1С передают не процедуру, а описание оповещения. Это основной приём клиентского программирования в управляемых формах.

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

&НаКлиенте
Процедура УдалениеПодтверждено(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = КодВозвратаДиалога.Да Тогда
        // удаление
    КонецЕсли;
КонецПроцедуры

Конструктор принимает имя процедуры строкой и контекст (ЭтотОбъект — текущая форма или модуль). Третий параметр — структура с данными, которые доедут до обработчика в ДополнительныеПараметры.

Теперь сам программный вызов. Если результат у вас уже есть и ждать диалога не нужно, запустите оповещение вручную:

ВыполнитьОбработкуОповещения(Оповещение, КодВозвратаДиалога.Да);

Процедура-обработчик получит значение как Результат. Так делают, когда ветка «пользователя спрашивать не надо» должна идти тем же путём, что и ответ из диалога. Код обработки пишется один раз.

Требования к обработчику простые:

  • первый параметр — результат, второй — дополнительные параметры;
  • процедура лежит в модуле, который указан во втором параметре конструктора;
  • в форме её делают клиентской (&НаКлиенте);
  • вызов из другого модуля требует Экспорт.

Оповестить и ОбработкаОповещения: событие для всех форм

Этот механизм нужен, когда одна форма должна сообщить другим: «элемент справочника записан, обновите списки».

Отправитель:

&НаКлиенте
Процедура ПослеЗаписи()
    Оповестить("ЗаписанКонтрагент", Объект.Ссылка, ЭтаФорма);
КонецПроцедуры

Получатель, в модуле любой открытой формы:

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

ИмяСобытия — произвольная строка, договоритесь о ней в проекте. Параметр — любое значение, Источник — кто послал.

Три тонкости:

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

Для выбора значений есть родственное ОповеститьОВыборе — оно запускает ОбработкаВыбора в форме-владельце.

Запуск событий объекта: Записать, Провести, Удалить

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

Объект = Ссылка.ПолучитьОбъект();
Объект.Комментарий = "Исправлено обработкой";
Объект.Записать();

После Записать() сработают ПередЗаписью и ПриЗаписи в модуле объекта, а следом подписки на соответствующие события. Для документов добавляются Провести() и ОбработкаПроведения. Если нужно запустить только проверку заполнения, зовут ПроверитьЗаполнение().

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

Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать();

При Загрузка = Истина платформа не выполняет часть логики, а в своих обработчиках вы начинаете их с Если Источник.ОбменДанными.Загрузка Тогда Возврат; КонецЕсли;. Но подписки и обработчики без такой проверки всё равно отработают. Поэтому в собственных обработчиках условие ставят первой строкой.

Массовая запись через Записать() в цикле по тысячам документов запускает все подписки на каждый объект. Проверьте на копии базы, сколько это занимает, и не запускайте обработку в рабочее время.

Подписки на события: как с ними работать из кода

Подписка настраивается в конфигураторе: ветка Общие → Подписки на события. В ней указано: на какие типы объектов она действует, какое событие слушает и какую процедуру вызывает.

Процедура подписки лежит в общем модуле и обязана быть экспортной. Параметры зависят от события. Для ПередЗаписью это Источник и Отказ, для документов дополнительно — режимы записи и проведения. Общий модуль должен быть серверным: подписка выполняется на сервере.

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

Что делать, если нужно «позвать подписку» руками? Три варианта по возрастанию аккуратности.

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

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

Временно отключить подписку из кода нельзя. Для этого используют флаг в дополнительных свойствах объекта: перед записью Объект.ДополнительныеСвойства.Вставить("ПропуститьПроверку", Истина), а в обработчике читают этот ключ и выходят. Контролируйте такие «выключатели»: через год никто не вспомнит, откуда они взялись.

Параметры событий: что передавать при ручном вызове

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

Параметры, которые обработчики принимают чаще всего:

  • 🔘 Элемент — элемент формы, вызвавший событие. Передавайте Элементы.ИмяПоля, если внутри читаются его свойства.
  • 🛑 Отказ — булево значение, которое обработчик может поставить в Истина. При ручном вызове заведите переменную Отказ = Ложь, передайте её и проверьте после вызова. Для процедур, получающих параметры по ссылке, это единственный способ узнать результат.
  • 📦 СтандартнаяОбработка — признак, что платформа продолжит работу по умолчанию. Обработчик НачалоВыбора ставит её в Ложь, когда открывает собственную форму выбора. Вручную эту логику не выполнить, потому что платформа после вызова ничего делать не будет.
  • 🔁 ДополнительныеПараметры — структура для обработчика оповещения, ваша собственная.

Короткий пример с Отказ:

&НаКлиенте
Процедура ПроверитьПередЗакрытием()
    Отказ = Ложь;
    ПередЗакрытием(Отказ, Ложь, "", Истина);
    Если Отказ Тогда
        Возврат;
    КонецЕсли;
    Закрыть();
КонецПроцедуры

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

Если у процедуры много параметров и вы вызываете её часто, оберните вызов в свою процедуру с понятным именем. Остальной код тогда не зависит от сигнатуры платформы.

Обработчики ожидания и другие отложенные вызовы

Иногда «вызвать событие» значит «вызвать через секунду». Для этого на клиенте есть обработчик ожидания:

ПодключитьОбработчикОжидания("ОбновитьДанные", 5, Ложь);

Процедура ОбновитьДанные будет вызываться каждые 5 секунд. Третий параметр — признак однократного срабатывания: Истина запустит её один раз. Снять подключение можно через ОтключитьОбработчикОжидания("ОбновитьДанные").

Правила, которые экономят время отладки:

  • процедура должна быть клиентской и без параметров;
  • минимальный интервал — 0,1 секунды;
  • одну процедуру подключают один раз, иначе вы получите дубли.

Чаще всего этот приём нужен, чтобы выполнить что-то после открытия формы, когда ПриОткрытии уже закончил работу. Подключите обработчик с интервалом 0.1 и однократным запуском. Так форма успеет полностью отрисоваться.

Типичные ошибки и отладка

Список ошибок короткий, но они встречаются постоянно.

Событие не срабатывает после присвоения из кода. Смотрите раздел про прямой вызов: программное изменение значения не запускает ПриИзменении.

«Метод объекта не обнаружен ». Вы вызвали процедуру другого модуля без Экспорт или обратились к форме, у которой её нет. Проверьте, что процедура экспортная, а модуль — тот, который нужно.

Серверный код вызывает клиентский метод. Например, Оповестить или ПоказатьВопрос в &НаСервере. Верните результат на клиент и вызовите оттуда.

Обработчик отработал дважды. Причин две: подписка и обработчик в модуле объекта делают одно и то же, либо обработчик ожидания подключён повторно.

Обработчик не видит данных формы. ДополнительныеПараметры не передали в ОписаниеОповещения. Создавайте структуру со всеми нужными значениями заранее.

Бесконечный цикл. Обработчик ПриИзменении меняет поле, а оно вызывает его снова через ваш ручной вызов. Закладывайте флаг-признак «уже выполняется».

Как найти, кто и когда вызвал обработчик

Откройте Конфигуратор и поставьте точку останова внутри обработчика. Запустите приложение в режиме отладки (Отладка → Начать отладку) и дождитесь остановки. В окне Стек вызовов будет видно всю цепочку: какая процедура, из какого модуля, с каким параметром. Для подписки в стеке сразу виден платформенный вызов, и становится понятно, что её запустила запись объекта.

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

Перед программным вызовом обработчика
Выполнено: 0 / 5

Частые вопросы

Можно ли программно вызвать событие ПриИзменении поля?
Само событие эмулировать нельзя, но можно вызвать его процедуру-обработчик напрямую по имени. Передайте нужный элемент формы или Неопределено, если параметр не читается.
Чем ОписаниеОповещения отличается от Оповестить?
ОписаниеОповещения связывает одно действие с одной процедурой и срабатывает после диалога или закрытия формы. Оповестить рассылает сообщение всем открытым формам, и каждая решает, нужно ли ей реагировать.
Как запустить подписку на событие вручную?
Выполните действие, на которое она настроена, например запишите объект. Если нужна только её логика, вызовите экспортную процедуру общего модуля с нужными параметрами, но события платформы при этом не будет.
Почему процедура обработчика ожидания не вызывается?
Чаще всего процедура написана не как клиентская, у неё есть параметры либо опечатка в имени. Имя в ПодключитьОбработчикОжидания указывают строкой, и компилятор опечатку не поймает.
Можно ли вызвать клиентский обработчик с сервера?
Напрямую нельзя. Сервер возвращает результат клиенту, и дальше клиентская процедура вызывает обработчик сама, например через ВыполнитьОбработкуОповещения.