Почему не срабатывает обработчик и форма выбора не возвращает значение в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В разработке на управляемых формах 1С часто встречается задача: из одной формы (например, обработки) нужно открыть другую (форму выбора документов или справочников), дать пользователю выбрать одно или несколько значений и вернуть их обратно в исходную форму. Для этого используется механизм оповещений. Однако иногда, несмотря на кажущуюся простоту кода, обработчик оповещения не срабатывает, и выбранное значение не возвращается. Разберем типичную ситуацию и выясним, в чем могут быть причины и как их исправить.

Проанализируем классический пример. У нас есть обработка, на форме которой размещена кнопка. По нажатию на эту кнопку должна открываться форма выбора заказов покупателей с возможностью множественного выбора. После того как пользователь выберет документы и закроет форму, мы ожидаем, что сработает наша процедура-обработчик и получит массив выбранных ссылок.

Вот первоначальный код, который не работает:


&НаКлиенте
Процедура Импорт(Команда)
    ПараметрыВыбора = Новый Структура;
    ПараметрыВыбора.Вставить("РежимВыбора", Истина);
    ПараметрыВыбора.Вставить("МножественныйВыбор", Истина);
    
    // Создаем описание оповещения, которое должно вызвать процедуру "ПриЗакрытииФормыВыбора"
    ОбработкаВыбора = Новый ОписаниеОповещения("ПриЗакрытииФормыВыбора", ЭтаФорма);
    
    ОткрытьФорму("Документ.ЗаказПокупателя.ФормаВыбора", ПараметрыВыбора, Команда, , , , ОбработкаВыбора);
КонецПроцедуры

&НаКлиенте
Процедура ПриЗакрытииФормыВыбора(Значение, ДопПараметры) Экспорт
    // Ставим точку останова, но отладчик сюда не заходит
    
    Если Значение = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    МассивДокументов = Значение;
КонецПроцедуры

При выполнении этого кода форма выбора открывается корректно, позволяет выбрать документы, но после ее закрытия ничего не происходит — точка останова в процедуре ПриЗакрытииФормыВыбора не срабатывает. Выясним, почему так происходит.

Причина 1: Неверная сигнатура процедуры-обработчика

Это самая неочевидная, но, как показывает практика, частая причина проблемы. Механизм оповещений в платформе 1С очень строго относится к соответствию сигнатур (количеству и порядку параметров) вызываемой процедуры и того, что ожидает система.

Давайте посмотрим на конструктор объекта ОписаниеОповещения:

Новый ОписаниеОповещения(<ИмяПроцедуры>, <Модуль>, <ДополнительныеПараметры>)

Когда мы создаем оповещение вот так:

ОбработкаВыбора = Новый ОписаниеОповещения("ПриЗакрытииФормыВыбора", ЭтаФорма);

...мы не передаем третий параметр ДополнительныеПараметры. Платформа ожидает, что после выполнения действия (закрытия формы выбора) она вызовет процедуру-обработчик, передав в нее сначала результат выбора, а затем — те самые дополнительные параметры, которые мы указали при создании оповещения.

Наша процедура-обработчик объявлена так:

Процедура ПриЗакрытииФормыВыбора(Значение, ДопПараметры) Экспорт

Она ожидает два обязательных параметра: Значение (результат выбора) и ДопПараметры. Но поскольку мы не передавали дополнительные параметры в конструктор ОписаниеОповещения, платформа пытается вызвать процедуру только с одним параметром (результатом выбора). Она не находит процедуру с подходящей сигнатурой (именем и одним параметром) и просто "молча" отбрасывает вызов, не выдавая никакой ошибки.

Решение: Сделать второй параметр в процедуре-обработчике необязательным, присвоив ему значение по умолчанию.


&НаКлиенте
Процедура ПриЗакрытииФормыВыбора(Значение, ДопПараметры = Неопределено) Экспорт
    // Теперь точка останова сработает!
    
    Если ТипЗнч(Значение) = Тип("Массив") Тогда
        Для каждого ЭлементМассива из Значение Цикл
            Сообщить(ЭлементМассива);
        КонецЦикла;
    ИначеЕсли Значение <> Неопределено Тогда
        Сообщить(Значение);
    КонецЕсли;
КонецПроцедуры

Теперь сигнатура процедуры удовлетворяет условиям вызова как с одним параметром, так и с двумя. Платформа успешно находит и выполняет ее.

Причина 2: Использование нетиповой формы выбора без `ОповеститьОВыборе`

Если вы открываете стандартную, типовую форму выбора (например, Документ.ЗаказПокупателя.ФормаВыбора), то платформа сама заботится о том, чтобы при выборе элемента или нажатии кнопки "Выбрать" был вызван метод ОповеститьОВыборе(). Этот метод и является тем триггером, который запускает механизм оповещения в вызывающей форме.

Однако, если вы создали свою собственную, нетиповую форму для выбора данных, вы должны самостоятельно позаботиться о вызове этого метода. Например, в обработчике команды кнопки "Выбрать" или при двойном клике на строке таблицы в вашей кастомной форме выбора нужно явно написать код:


&НаКлиенте
Процедура ВыбратьИЗакрыть(Команда)
    // Получаем текущие данные, которые нужно вернуть
    ВыбранноеЗначение = Элементы.Список.ТекущиеДанные.Ссылка;

    // Оповещаем вызывающую форму о сделанном выборе
    ОповеститьОВыборе(ВыбранноеЗначение);

    // Закрывать форму после ОповеститьОВыборе() не обязательно,
    // если она была открыта модально, она закроется сама.
    // Но если она немодальная, ее можно закрыть вручную.
    Закрыть();
КонецПроцедуры

Без вызова ОповеститьОВыборе(ВыбранноеЗначение) в кастомной форме выбора вызывающая форма никогда не узнает, что выбор был сделан. Чтобы не создавать подобные диалоги вручную, можно применить универсальную форму для ввода нескольких значений различных типов.

Причина 3: Альтернативный и более "классический" способ через `ОбработкаВыбора`

Механизм ОписаниеОповещения является гибким, но для простой задачи получения результата выбора существует более старый и надежный способ, встроенный в саму платформу. Он заключается в использовании события ОбработкаВыбора.

Чтобы он сработал, нужно выполнить два условия:

  1. При вызове метода ОткрытьФорму обязательно указать четвертый параметр — Владелец. В качестве владельца передается сама вызывающая форма: ЭтаФорма.
  2. В модуле вызывающей формы создать процедуру-обработчик события с именем ОбработкаВыбора.

Посмотрим на пример:

В этом случае код вызова формы выбора упрощается, так как нам больше не нужен объект ОписаниеОповещения.


&НаКлиенте
Процедура Импорт(Команда)
    ПараметрыВыбора = Новый Структура;
    ПараметрыВыбора.Вставить("РежимВыбора", Истина);
    ПараметрыВыбора.Вставить("МножественныйВыбор", Истина);
    
    // Открываем форму, указывая ЭтаФорма в качестве владельца.
    // ОписаниеОповещения больше не нужно.
    ОткрытьФорму("Документ.ЗаказПокупателя.ФормаВыбора", ПараметрыВыбора, ЭтаФорма);
КонецПроцедуры

А в модуле этой же формы добавляем стандартный обработчик:


&НаКлиенте
Процедура ОбработкаВыбора(ВыбранноеЗначение, ИсточникВыбора)
    // Платформа автоматически вызовет эту процедуру после выбора
    // в дочерней форме.
    
    Если ВыбранноеЗначение = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // ВыбранноеЗначение будет содержать либо один элемент, либо массив,
    // если был установлен МножественныйВыбор.
    МассивДокументов = ВыбранноеЗначение;
    Сообщить("Выбрано документов: " + МассивДокументов.Количество());
КонецПроцедуры

Этот способ считается более "родным" для платформы и часто является предпочтительным, так как он проще в реализации и менее подвержен ошибкам, связанным с сигнатурами процедур. Параметр ИсточникВыбора в обработчике содержит уникальный идентификатор или имя элемента формы, который инициировал выбор, что может быть полезно, если на форме несколько полей выбора.

Таким образом, если ваша форма выбора не возвращает значение, в первую очередь проверьте сигнатуру процедуры-обработчика, а также рассмотрите возможность использования стандартного механизма ОбработкаВыбора, указав владельца при открытии формы.

← На главную