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