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