Как устроен выбор значения в управляемых формах

· 9 мин

Содержание

Обработка выбора в 1С:Предприятие 8.3 — это цепочка событий, которая срабатывает, когда пользователь заполняет поле ссылочного типа: от нажатия кнопки выбора до возврата готового значения в реквизит формы. Управляют ею два события поля — НачалоВыбора (открытие формы выбора) и ОбработкаВыбора (приём выбранного значения). Между ними работает сама форма выбора, которая отдаёт результат методом ОповеститьОВыборе.

Разберём механизм по шагам: кто и в каком порядке получает управление, как подставить свою форму выбора, ограничить список отбором, спозиционировать его на нужной строке и корректно обработать реквизиты составного типа. Все примеры — для управляемых форм платформы 8.3 с интерфейсом «Такси».

Из чего складывается цепочка выбора значения

Выбор значения — это не одно событие, а последовательность. Для поля ввода, связанного с реквизитом ссылочного типа, она выглядит так:

  1. Пользователь нажимает кнопку выбора (...), клавишу F4 или вводит текст и жмёт выбор.
  2. На поле формы срабатывает событие НачалоВыбора. По умолчанию платформа открывает форму выбора соответствующего справочника или документа.
  3. Форма выбора открывается в режиме выбора (РежимВыбора = Истина). Пользователь отмечает строку.
  4. Форма выбора вызывает ОповеститьОВыборе(Значение) и закрывается, передавая выбранное значение владельцу.
  5. На исходном поле срабатывает событие ОбработкаВыбора, куда приходит выбранное значение. Платформа записывает его в связанный реквизит.

Ключевое: НачалоВыбора открывает окно, ОбработкаВыбора принимает результат. Если нужно поменять поведение — вклинивайтесь именно в эти два события и отключайте стандартную логику флагом СтандартнаяОбработка = Ложь.

В каждом из них есть параметр СтандартнаяОбработка. Он приходит со значением Истина — платформа сделает всё сама. Присвойте ему Ложь, и дальше отработает только ваш код.

Какие события поля отвечают за выбор

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

СобытиеКогда срабатываетДля чего использовать
НачалоВыбораНажата кнопка выбора или F4Открыть свою форму, подставить отбор
ОбработкаВыбораИз формы выбора вернулось значениеПроверить и доработать результат
ОкончаниеВводаТекстаВведён текст, его надо превратить в ссылкуСвой поиск по строке
АвтоПодборПользователь набирает символыПодсказка списком при вводе

Перехват НачалоВыбора со своей формой выглядит так:

&НаКлиенте
Процедура КонтрагентНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;

    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("РежимВыбора", Истина);

    ОткрытьФорму("Справочник.Контрагенты.ФормаВыбора",
        ПараметрыФормы, Элемент);
КонецПроцедуры

Третьим параметром ОткрытьФорму передаётся владелец — здесь это сам Элемент. Именно он получит событие ОбработкаВыбора, когда форма вернёт значение:

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

Если оставить СтандартнаяОбработка = Истина, платформа сама запишет ВыбранноеЗначение в реквизит. Отключаете её — записываете значение в реквизит сами.

Выбор без открытия окна: ввод по строке

Часть выборов проходит мимо формы списка. Пользователь набирает несколько символов прямо в поле, и платформа пытается найти подходящий элемент. За это отвечают два события:

  • АвтоПодбор — срабатывает по мере набора и формирует выпадающий список подсказок. Здесь можно подменить стандартный подбор своим, если нужно искать не только по наименованию, но, например, по коду или ИНН.
  • ОкончаниеВводаТекста — срабатывает, когда ввод закончен и строку нужно превратить в ссылку. Если по тексту нашлось несколько элементов, платформа предложит выбрать из них.
&НаКлиенте
Процедура КонтрагентОкончаниеВводаТекста(Элемент, Текст, ДанныеВыбора, Параметры, СтандартнаяОбработка)
    Если ПустаяСтрока(Текст) Тогда
        Возврат;
    КонецЕсли;
    // здесь можно подставить свой список ДанныеВыбора по введённой строке
КонецПроцедуры

Для большинства типовых решений менять эти события не нужно — стандартный ввод по наименованию работает сразу. Трогайте их, только когда нужен нестандартный поиск.

Современный способ: оповещение с обратным вызовом

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

&НаКлиенте
Процедура ВыбратьКонтрагента(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеВыбораКонтрагента", ЭтотОбъект);

    ПараметрыФормы = Новый Структура("РежимВыбора", Истина);
    ОткрытьФорму("Справочник.Контрагенты.ФормаВыбора",
        ПараметрыФормы, , , , , Оповещение);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораКонтрагента(Результат, ДополнительныеПараметры) Экспорт
    Если Результат <> Неопределено Тогда
        Объект.Контрагент = Результат;
    КонецЕсли;
КонецПроцедуры

Седьмой параметр ОткрытьФорму — это описание оповещения о закрытии. Когда пользователь ничего не выбрал и закрыл окно, в Результат придёт Неопределено — проверку на это ставьте всегда.

Как форма выбора отдаёт результат

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

&НаКлиенте
Процедура СписокВыбор(Элемент, Строка, Поле, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;

    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        ОповеститьОВыборе(ТекущиеДанные.Ссылка);
    КонецЕсли;
КонецПроцедуры

ОповеститьОВыборе закрывает форму и передаёт значение владельцу. Дальше у владельца срабатывает либо ОбработкаВыбора поля, либо процедура обратного вызова — смотря как открывали.

Проверяйте ТекущиеДанные на Неопределено. Если список пуст или фокус не на строке, обращение к ТекущиеДанные.Ссылка вызовет ошибку.

У самой формы-владельца есть и событие формы ОбработкаВыбора(ВыбранноеЗначение, Источник) — оно ловит оповещения от подчинённых форм, открытых без отдельного описания оповещения. Параметр Источник подсказывает, какая именно форма вернула значение, если их несколько.

Как ограничить список отбором

Чаще всего задача звучит так: показать не весь справочник, а только подходящие элементы — договоры этого контрагента, номенклатуру этого вида, склады этой организации. Для этого есть два механизма.

Параметры выбора и связи параметров выбора. Настраиваются в конфигураторе у реквизита или у поля формы. Связь параметров выбора (СвязиПараметровВыбора) привязывает отбор к значению другого поля: выбрали контрагента — список договоров автоматически сузился до его договоров. Это самый надёжный путь, код не нужен.

Отбор при открытии формы. Когда перехватываете НачалоВыбора, передайте структуру отбора в параметрах формы:

&НаКлиенте
Процедура ДоговорНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    Если НЕ ЗначениеЗаполнено(Объект.Контрагент) Тогда
        СтандартнаяОбработка = Ложь;
        Предупреждение("Сначала укажите контрагента.");
        Возврат;
    КонецЕсли;

    СтандартнаяОбработка = Ложь;
    Отбор = Новый Структура("Владелец", Объект.Контрагент);

    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("Отбор", Отбор);
    ПараметрыФормы.Вставить("РежимВыбора", Истина);

    ОткрытьФорму("Справочник.ДоговорыКонтрагентов.ФормаВыбора",
        ПараметрыФормы, Элемент);
КонецПроцедуры

Форма выбора на основе динамического списка умеет применять переданный Отбор к своему списку. Для более сложных условий (не равенство, а «в списке» или «больше») настраивайте отбор динамического списка в самой форме выбора по параметрам, которые туда приходят.

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

Позиционирование списка на нужной строке

Когда открываете форму выбора, разумно сразу поставить курсор на текущее значение поля — чтобы пользователь видел, что уже выбрано, и мог быстро поменять. За это отвечает параметр формы ТекущаяСтрока:

&НаКлиенте
Процедура НоменклатураНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;

    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("РежимВыбора", Истина);
    Если ЗначениеЗаполнено(Объект.Номенклатура) Тогда
        ПараметрыФормы.Вставить("ТекущаяСтрока", Объект.Номенклатура);
    КонецЕсли;

    ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора",
        ПараметрыФормы, Элемент);
КонецПроцедуры

Динамический список формы выбора прочитает ТекущаяСтрока и спозиционируется на этой ссылке. Если значение не заполнено — не передавайте параметр, иначе список попробует встать на «пустую» строку.

Для иерархических справочников полезно открыть сразу нужную группу. Заранее настройте в форме выбора позиционирование по родителю выбранного элемента — тогда пользователь откроется прямо в его папке, а не в корне. Для этого в форме выбора в обработчике открытия считайте пришедшую ТекущаяСтрока, определите её родителя и установите его как текущую группу динамического списка.

Ещё один полезный приём — запрет перехода по иерархии, когда выбор должен идти только из конкретной группы. Установите у динамического списка формы выбора отбор по родителю и уберите у списка режим просмотра «дерево»: пользователь увидит плоский список нужных элементов и не сможет случайно выбрать что-то из соседней папки.

Выбор для реквизитов составного типа

Если реквизит имеет составной тип (например, и СправочникСсылка.ФизическиеЛица, и СправочникСсылка.Контрагенты), выбор работает иначе. При нажатии кнопки платформа сначала показывает меню выбора типа, и только потом открывает форму выбора конкретного справочника.

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

&НаКлиенте
Процедура ПолучательНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;

    СписокТипов = Новый СписокЗначений;
    СписокТипов.Добавить("Справочник.Контрагенты.ФормаВыбора", "Контрагент");
    СписокТипов.Добавить("Справочник.ФизическиеЛица.ФормаВыбора", "Физлицо");

    Оповещение = Новый ОписаниеОповещения("ПослеВыбораТипа", ЭтотОбъект);
    СписокТипов.ПоказатьВыборЭлемента(Оповещение, "Кого выбираем?");
КонецПроцедуры

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

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

&НаКлиенте
Процедура ПослеВыбораПолучателя(Результат, ДопПараметры) Экспорт
    Если Результат <> Неопределено Тогда
        Объект.Получатель = Результат;
    КонецЕсли;
КонецПроцедуры

В событии ОбработкаВыбора для составного реквизита всегда проверяйте ТипЗнч(ВыбранноеЗначение) — так вы не запишете в поле значение неподходящего типа и не получите ошибку на записи объекта.

Производительность и типичные ошибки

Механизм выбора редко тормозит сам по себе — узкие места появляются в форме выбора и в коде-перехватчике.

  • 🐌 Тяжёлый отбор динамического списка. Если форма выбора открывается с отбором по вычисляемому полю или по неиндексированному реквизиту, список строится долго. Отбирайте по полям, которые есть в индексе, или через связи параметров выбора.
  • 🔁 Лишние запросы в ОбработкаВыбора. Не ходите в базу на каждый выбор, если можно взять данные из уже загруженной формы. Для получения связанных реквизитов используйте один пакетный запрос, а не цикл с обращениями по одной ссылке.
  • 🧩 Серверные вызовы с клиента. Код НачалоВыбора и ОбработкаВыбора исполняется на клиенте. Обращение к справочникам и регистрам требует серверного контекста — выносите его в процедуру &НаСервере и зовите одним вызовом, а не несколькими подряд.

Частые ошибки, из-за которых выбор «не работает »:

Диагностика: выберите симптом

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

Проверьте, что поняли механизм

Проверьте себя
Какое событие поля принимает уже выбранное значение?
Что нужно сделать, чтобы платформа не открывала стандартную форму выбора?
Как форма выбора возвращает значение владельцу?

Частые вопросы

Чем `НачалоВыбора` отличается от `ОбработкаВыбора`?
НачалоВыбора срабатывает при нажатии кнопки выбора и отвечает за открытие окна. ОбработкаВыбора срабатывает, когда из окна вернулось значение, и отвечает за его приём в поле.
Как ловить результат, если открываю форму из команды, а не из поля?
Через ОписаниеОповещения — седьмым параметром ОткрытьФорму. Платформа вызовет вашу процедуру обратного вызова и передаст в неё выбранное значение или Неопределено.
Можно ли открыть не форму выбора, а обычную форму списка?
Да. Передайте в параметрах РежимВыбора = Истина, и форма списка будет работать как форма выбора, даже если отдельная форма выбора не создана.
Почему при составном типе сначала появляется меню выбора типа?
Платформа не знает, из какого справочника брать значение, и предлагает выбрать тип. Перехватив НачалоВыбора, вы можете показать своё меню типов или сразу открыть нужную форму.
Как передать в форму выбора несколько условий отбора?
Соберите их в одну структуру Отбор и передайте в параметрах формы. Форму выбора на динамическом списке настройте так, чтобы она применяла пришедшие поля к отбору списка.