Как программно изменить или дополнить существующий обработчик события в 1С

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

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

Постановка задачи и основные методы

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

Для манипуляций с обработчиками событий элементов формы в языке 1С предусмотрены два ключевых метода:

  1. ПолучитьДействие(ИмяСобытия) — позволяет узнать, какая процедура назначена обработчиком в данный момент.
  2. УстановитьДействие(ИмяСобытия, Действие) — назначает новую процедуру для обработки события.

Проанализируем специфику применения этих методов в разных типах форм, так как поведение системы существенно различается.

Особенности работы в обычных и управляемых формах

Разберем различия, которые критически важны для написания работоспособного кода:

В обычных формах: Метод УстановитьДействие ожидает в качестве второго параметра объект типа Действие. При получении текущего действия через ПолучитьДействие мы также получаем этот объект, из которого можно извлечь имя процедуры.

В управляемых формах: Платформа 8.3 работает со строками. Метод ПолучитьДействие("ПриИзменении") вернет просто строку с именем процедуры. Если обработчик не задан, будет возвращена пустая строка. Метод УстановитьДействие также принимает имя процедуры в виде строки.

Реализация через процедуру-обертку

Наиболее универсальный способ, который обсуждался в сообществе, — это создание так называемой «процедуры-обертки». Рассмотрим этот метод по шагам.

Предположим, у нас есть элемент формы ПолеВвода1. Нам нужно дополнить его логику. Проанализируем алгоритм:

  1. При открытии формы получаем имя старого обработчика.
  2. Назначаем свою новую процедуру.
  3. Внутри новой процедуры вызываем старую (если она была).

Пример кода для реализации этой логики:


// В модуле формы в процедуре ПриОткрытии (или ПриСозданииНаСервере для управляемых форм)
СтароеДействие = Элементы.ПолеВвода1.ПолучитьДействие("ПриИзменении");
// Сохраним имя старого действия в переменную модуля или реквизит формы, 
// чтобы вызвать его позже
ИмяСтарогоОбработчика = СтароеДействие; 
Элементы.ПолеВвода1.УстановитьДействие("ПриИзменении", "МойНовыйОбработчикПриИзменении");

Теперь создадим саму процедуру-обертку в модуле формы:


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

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

Опасность потери параметров

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

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


&НаКлиенте
Процедура МойОбработчик_ПриИзменении(Элемент)
    // Передаем элемент дальше в старую процедуру
    Выполнить(ИмяСтарогоОбработчика + "(Элемент)");
КонецПроцедуры

Современный подход: Использование расширений

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

Рассмотрим два основных механизма расширений для нашей задачи:

  1. Аннотация &После: Ваша процедура будет вызвана автоматически сразу после завершения работы основной процедуры. Это идеально подходит для добавления проверок или уведомлений.
  2. Аннотация &Вместо: Вы полностью заменяете стандартный метод. Однако внутри вы можете вызвать оригинальный код с помощью команды ПродолжитьВызов().

Проанализируем пример использования &Вместо, который является самым гибким инструментом:


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

    // Вызов оригинального кода разработчика
    ПродолжитьВызов(Элемент);
    
    // Код, который выполнится ПОСЛЕ основного алгоритма
    ОбновитьСвязанныеДанные();
    
КонецПроцедуры

Использование ПродолжитьВызов() гораздо безопаснее, так как вам не нужно знать точное имя исходной процедуры или программно манипулировать свойствами элементов формы. Система сама определит цепочку вызовов.

Когда программное назначение необходимо?

Несмотря на наличие расширений, метод УстановитьДействие остается незаменимым в случае программного создания элементов формы. Если вы динамически добавляете поле ввода на форму в коде (например, в ПриСозданииНаСервере), событие ПриИзменении для него по умолчанию не будет обрабатываться. В этом случае вам обязательно нужно явно указать обработчик:


НовыйЭлемент = Элементы.Добавить("ПолеКонтрагент", Тип("ПолеФормы"), ГруппаШапка);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = "Объект.Контрагент";
// Назначаем обработчик
НовыйЭлемент.УстановитьДействие("ПриИзменении", "Процедура_ПриИзмененииКонтрагента");

Резюме

Выясним краткие итоги нашего анализа:

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

← На главную