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