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