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