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