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