При динамической генерации элементов управляемой формы разработчики часто сталкиваются с ситуацией, когда назначенные через метод УстановитьДействие процедуры не срабатывают или вызывают ошибки выполнения. Типичный сценарий — вывод матрицы или расписания с динамическим числом колонок, где требуется отследить, на какую именно ячейку нажал пользователь, определить имя колонки и номер строки.
Разберем по шагам, почему программно назначенные обработчики могут игнорироваться платформой, как правильно связать события с кодом и как гарантированно получить координаты выбранной ячейки и содержащиеся в ней данные.
Выясним основные причины, из-за которых код события не вызывается при клике или изменении поля:
ПриИзменении поля ввода требует ровно один параметр — сам элемент управления. Если объявить процедуру без параметров вида Процедура ИмяОбработчика(), платформа не сможет передать управление и выдаст ошибку несоответствия параметров либо проигнорирует вызов.&НаКлиенте. Без неё процедура компилируется на сервере, куда клиентские события элементов управления не маршрутизируются.ПриИзменении существует у элементов ввода данных (полей формы), но его нет у самого контейнера — объекта ТаблицаФормы. Попытка вызвать метод УстановитьДействие("ПриИзменении", ...) для таблицы приведет к ошибке во время исполнения.УстановитьДействие, должна символ в символ совпадать с объявленным именем экспортной или локальной процедуры модуля формы.Если задача заключается именно в реакции на ввод или редактирование значения, действие привязывается к каждому программно созданному полю ввода внутри цикла создания колонок.
Посмотрим на пример правильного серверного кода создания колонок и привязки действия:
&НаСервере
Процедура СоздатьКолонкиТаблицыНаСервере()
ТаблицаПолейВыбора = Элементы.Найти("ТЗН");
Для Каждого Колонка Из ТабличкаДанных.Колонки Цикл
НовоеПоле = Элементы.Добавить(Колонка.Имя, Тип("ПолеФормы"), ТаблицаПолейВыбора);
НовоеПоле.Вид = ВидПоляФормы.ПолеВвода;
НовоеПоле.ПутьКДанным = "Табличка." + Колонка.Имя;
// Назначаем клиентский обработчик
НовоеПоле.УстановитьДействие("ПриИзменении", "Колонка_ПриИзменении");
КонецЦикла;
КонецПроцедуры
Теперь объявим обработчик на клиенте с корректной сигнатурой, принимающей элемент управления:
&НаКлиенте
Процедура Колонка_ПриИзменении(Элемент)
// 1. Имя выбранной колонки получаем напрямую из элемента
ИмяКолонки = Элемент.Имя;
// 2. Получаем строку таблицы через элемент таблицы формы
ТаблицаФормы = Элементы.ТЗН;
ИдентификаторСтроки = ТаблицаФормы.ТекущаяСтрока;
// 3. Получаем данные текущей активной строки
ТекущиеДанныеСтроки = ТаблицаФормы.ТекущиеДанные;
Если ТекущиеДанныеСтроки <> Неопределено Тогда
ЗначениеЯчейки = ТекущиеДанныеСтроки[ИмяКолонки];
Сообщить("Изменена колонка: " + ИмяКолонки + ", значение: " + Строка(ЗначениеЯчейки));
КонецЕсли;
КонецПроцедуры
Когда редактирование ячеек пользователю запрещено, а требуется реагировать на двойной клик или нажатие клавиши Enter (например, для открытия детального расписания или карточки записи), навешивать обработчики на каждое поле не требуется. Намного эффективнее назначить обработчик штатному событию Выбор самой таблицы формы.
Проанализируем назначение действия для таблицы формы на сервере:
&НаСервере
Процедура НастроитьСобытияТаблицы()
ТаблицаФормы = Элементы.ТЗН;
ТаблицаФормы.УстановитьДействие("Выбор", "ТЗН_Выбор");
КонецПроцедуры
Событие Выбор у таблицы формы передает в клиентскую процедуру готовые параметры: ссылку на выбранную строку и поле (колонку), по которому был произведен клик:
&НаКлиенте
Процедура ТЗН_Выбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
// Отменяем стандартный вход в режим редактирования ячейки
СтандартнаяОбработка = Ложь;
ИмяКолонки = Поле.Имя;
// Получаем строку реквизита формы по идентификатору
СтрокаДанных = Табличка.НайтиПоИдентификатору(ВыбраннаяСтрока);
Если СтрокаДанных <> Неопределено Тогда
ЗначениеЯчейки = СтрокаДанных[ИмяКолонки];
ИндексСтроки = Табличка.Индекс(СтрокаДанных);
Сообщить("Клик по колонке: " + ИмяКолонки + ", строка №: " + Строка(ИндексСтроки + 1));
Сообщить("Значение в ячейке: " + Строка(ЗначениеЯчейки));
КонецЕсли;
КонецПроцедуры
Если реакция интерфейса должна происходить при обычном одиночном клике мыши или при перемещении по таблице стрелками клавиатуры, используем событие таблицы ПриАктивизацииЯчейки.
Разберем клиентский обработчик для этого случая:
&НаКлиенте
Процедура ТЗН_ПриАктивизацииЯчейки(Элемент)
// Элемент в данном случае — это ТаблицаФормы
ТекущаяКолонка = Элемент.ТекущийЭлемент; // Объект ПолеФормы
ТекущиеДанные = Элемент.ТекущиеДанные; // Данные строки формы
Если ТекущаяКолонка <> Неопределено И ТекущиеДанные <> Неопределено Тогда
ИмяКолонки = ТекущаяКолонка.Имя;
ЗначениеЯчейки = ТекущиеДанные[ИмяКолонки];
КонецЕсли;
КонецПроцедуры
Рассмотрим подводные камни, возникающие при динамической генерации полей и строк:
ТекущаяСтрока и параметр ВыбраннаяСтрока возвращают уникальный числовой идентификатор строки в контексте формы, а не ее физический порядковый номер. Чтобы получить сам индекс строки, всегда используйте метод коллекции НайтиПоИдентификатору(), а затем метод Индекс().ИзменитьРеквизиты. Надежнее генерировать системные имена вида Колонка_1, Колонка_2, а само ФИО помещать в свойство Заголовок реквизита и поля формы.ИзменитьРеквизиты не заменяет существующие реквизиты. При повторном построении таблицы необходимо передавать созданные ранее реквизиты во второй параметр метода (массив удаляемых реквизитов) и удалять элементы формы через Элементы.Удалить(), иначе возникнет исключение о дублировании имен.