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

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

При разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью динамического изменения интерфейса. Типичная задача: программно добавить реквизит, создать поле на форме и назначить ему обработчик события, например, ПриИзменении. Однако при попытке вынести этот код в общий модуль стандартный метод УстановитьДействие() может перестать работать. Проанализируем, почему это происходит и как правильно реализовать эту задачу.

В чем заключается проблема?

Рассмотрим стандартную ситуацию: разработчик пытается добавить поле в табличную часть Товары и назначить ему обработчик из общего модуля. Код выглядит примерно так:


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

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

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

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

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

Решение №1: Использование «Прокси-процедуры» (Заглушки)

Наиболее надежный и стандартный способ решить проблему — создать небольшую процедуру-посредник (прокси) непосредственно в модуле формы. Проанализируем этот паттерн подробнее.

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


&НаКлиенте
Процедура ТоварыЦенаСоСкидкойПриИзменении(Элемент) Экспорт
    // Вызываем логику из общего модуля
    ОбщийМодульКлиент.ОбработатьИзменениеЦены(Элемент);
КонецПроцедуры

Шаг 2. В вашем программном коде (даже если он находится в общем модуле) укажите имя этой процедуры-посредника:


НовыйЭлемент.УстановитьДействие("ПриИзменении", "ТоварыЦенаСоСкидкойПриИзменении");

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

Решение №2: Использование расширений конфигурации

Если ваша задача — добавить функционал в типовую форму, не изменяя саму конфигурацию, лучше всего воспользоваться механизмом расширений. Это позволит обойти ограничение «чистого» общего модуля.

Разберем алгоритм действий по шагам:

  1. Создайте новое расширение и заимствуйте в него нужную форму.
  2. В модуле формы расширения добавьте обработчик события (например, через &После("ПриСозданииНаСервере")).
  3. Там же в модуле формы (в расширении) опишите процедуру-обработчик события ПриИзменении.
  4. Теперь при использовании УстановитьДействие() платформа увидит вашу процедуру, так как она фактически станет частью модуля формы после компиляции расширения.

Важные технические нюансы метода УстановитьДействие()

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

1. Доступность метода: В официальной справке часто можно встретить пометку «Доступность: Толстый клиент». Однако в управляемом приложении метод УстановитьДействие() доступен и в тонком клиенте, но работает он иначе. Если в обычном приложении вы передавали объект типа Действие, то в управляемом — строго строку с именем.

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

3. Работа с командами: Не путайте события элементов формы и команды. Для кнопок вы программно создаете объект КомандаФормы, у которой есть свойство Действие. Там также указывается имя процедуры модуля формы в виде строки.

Пример полного цикла программного создания реквизита и элемента

Посмотрим на полный пример кода, который должен быть выполнен НаСервере (например, в процедуре ПриСозданииНаСервере), чтобы корректно добавить реквизит в таблицу и связать его с событием:


// 1. Описываем новый реквизит для табличной части
ДобавляемыеРеквизиты = Новый Массив;
КЧ = Новый КвалификаторыЧисла(15, 2);
ТипРеквизита = Новый ОписаниеТипов("Число", , , КЧ);
РеквизитФормы = Новый РеквизитФормы("ЦенаСоСкидкой", ТипРеквизита, "Объект.Товары", "Цена со скидкой");
ДобавляемыеРеквизиты.Добавить(РеквизитФормы);

// 2. Добавляем реквизит на форму
ЭтаФорма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);

// 3. Создаем элемент интерфейса (поле ввода в колонке таблицы)
НовыйЭлемент = ЭтаФорма.Элементы.Добавить("ТоварыЦенаСоСкидкой", Тип("ПолеФормы"), ЭтаФорма.Элементы.Товары);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = "Объект.Товары.ЦенаСоСкидкой";

// 4. Назначаем обработчик, который обязан быть в модуле формы!
НовыйЭлемент.УстановитьДействие("ПриИзменении", "Подключаемый_ТоварыЦенаСоСкидкойПриИзменении");

Важно: Процедура Подключаемый_ТоварыЦенаСоСкидкойПриИзменении должна быть объявлена в модуле формы с директивой &НаКлиенте и ключевым словом Экспорт.

Почему нельзя использовать ОписаниеОповещения?

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

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

← На главную