Как предоставить пользователю выбрать строку из таблицы значений в управляемом приложении 1С

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

При переходе со старых версий платформы «1С:Предприятие» (например, 1С 7.7 или обычных форм 1С 8.2) на современные управляемые формы разработчики часто сталкиваются с тем, что привычные инструменты перестают работать. Один из классических примеров — метод ВыбратьСтроку(), принадлежащий объекту ТаблицаЗначений. Попытка вызвать его в тонком клиенте управляемого приложения приводит к ошибке выполнения.

В рамках этой статьи мы подробно разберем, почему старый подход больше не жизнеспособен, проанализируем архитектурные особенности клиент-серверного взаимодействия в 1С 8.3 и рассмотрим три надежных, современных способа реализации интерактивного выбора строки из таблицы значений.

Выясняем причину: почему ТЗ.ВыбратьСтроку() не работает?

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

  1. Разделение контекста. Объект типа ТаблицаЗначений физически отсутствует на клиенте. Он является исключительно серверным типом данных. Клиентский код «не знает» о существовании таблицы значений, работая вместо этого с ее подготовленным интерфейсным представлением — объектом типа ДанныеФормыКоллекция.
  2. Запрет интерактивности на сервере. Серверный контекст предназначен исключительно для выполнения быстрых математических, логических и транзакционных операций с базой данных. На сервере запрещено вызывать любые диалоговые окна, предупреждения, вопросы или формы выбора. Попытка вызвать метод ВыбратьСтроку() на сервере (как в шутку или по ошибке предлагалось в Сообщении 11 форума) вызовет исключение, так как серверный поток не может взаимодействовать с экраном пользователя.
  3. Тонкий клиент ориентирован на асинхронность. Любое ожидание действий пользователя должно быть реализовано через специализированные механизмы оповещения, чтобы не блокировать рабочий поток приложения.

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

Рассмотрим подробнее три основных способа решения этой задачи в современных конфигурациях на платформе «1С:Предприятие 8.3».

---

Способ 1. Создание вспомогательной управляемой формы выбора (универсальный метод)

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

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

Давайте разберем этот процесс по шагам.

Шаг 1. Подготовка и передача данных на сервере

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

Проанализируем пример кода на стороне вызывающей формы:


&НаКлиенте
Асинхронная Процедура ВыполнитьВыборСтроки(Команда)

    // Шаг 1.1: Подготавливаем ТЗ на сервере и помещаем во временное хранилище
    АдресХранилища = ПодготовитьТЗНаСервере();
    
    // Шаг 1.2: Создаем описание оповещения для обработки выбора пользователя
    ПараметрыОповещения = Новый ОписаниеОповещения("ПослеВыбораСтроки", ЭтотОбъект);
    
    // Шаг 1.3: Формируем параметры для открытия нашей формы выбора
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("АдресДанных", АдресХранилища);
    
    // Шаг 1.4: Открываем форму выбора асинхронно
    ОткрытьФорму("ОбщаяФорма.ФормаВыбораИзТЗ", ПараметрыФормы, ЭтотОбъект, , , , ПараметрыОповещения, РежимОткрытияОкнаФормы.БлокироватьВесьИнтерфейс);

КонецПроцедуры

&НаСервере
Функция ПодготовитьТЗНаСервере()

    // Создаем тестовую ТЗ для демонстрации
    ТЗ = Новый ТаблицаЗначений;
    ТЗ.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Число"));
    ТЗ.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
    ТЗ.Колонки.Добавить("ДополнительнаяИнформация", Новый ОписаниеТипов("Строка"));
    
    // Заполняем данными
    НоваяСтрока = ТЗ.Добавить();
    НоваяСтрока.Идентификатор = 1;
    НоваяСтрока.Наименование = "Первый вариант";
    НоваяСтрока.ДополнительнаяИнформация = "Описание первого варианта";
    
    НоваяСтрока = ТЗ.Добавить();
    НоваяСтрока.Идентификатор = 2;
    НоваяСтрока.Наименование = "Второй вариант";
    НоваяСтрока.ДополнительнаяИнформация = "Описание второго варианта";
    
    // Помещаем во временное хранилище с привязкой к УИД текущей формы
    Возврат ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);

КонецФункции

Шаг 2. Разработка формы выбора

Теперь нам нужно создать саму форму выбора. Это может быть как общая форма, так и произвольная форма внутри конкретной обработки или документа. Назовем ее ФормаВыбораИзТЗ. Для реализации формы с динамическим составом колонок можно использовать инструмент для программного вывода таблицы значений на управляемую форму.

  1. Создайте на этой форме реквизит с именем ДеревоДанных или ТаблицаВыбора и типом данных ТаблицаЗначений.
  2. Добавьте в этот реквизит те же колонки, которые присутствуют в вашей исходной таблице (в нашем примере: Идентификатор, Наименование, ДополнительнаяИнформация).
  3. Перетащите этот реквизит на рабочую область формы, чтобы платформа автоматически создала элемент интерфейса типа «Таблица».

Теперь напишем обработчик загрузки данных при создании формы на сервере:


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

Шаг 3. Обработка выбора строки и закрытие формы

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

Перейдите в свойства элемента формы «ТаблицаВыбора» и создайте обработчик события Выбор:


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

Шаг 4. Получение результата в вызывающей форме

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


&НаКлиенте
Процедура ПослеВыбораСтроки(РезультатВыбора, ДополнительныеПараметры) Экспорт

    // Проверяем, выбрал ли пользователь значение или просто закрыл окно
    Если РезультатВыбора <> Неопределено Тогда
        
        // Выводим сообщение с результатом выбора для демонстрации
        ТекстСообщения = Шаблон("Вы выбрали строку с ID: %1 и наименованием: %2", 
            РезультатВыбора.Идентификатор, 
            РезультатВыбора.Наименование);
            
        ПоказатьПредупреждение(, ТекстСообщения);
        
        // Здесь вы можете продолжить обработку данных на сервере, передав туда Идентификатор
        ОбработатьВыборНаСервере(РезультатВыбора.Идентификатор);
        
    КонецЕсли;

КонецПроцедуры

Этот способ является абсолютно безопасным, производительным и полностью соответствует концепции тонкого клиента 1С.

---

Способ 2. Использование списка значений для простых списков

Бывают ситуации, когда таблица значений по сути является плоским одномерным списком (например, содержит только одну значимую колонку «Наименование» или «Ссылка»). Создавать ради этого целую форму выбора не имеет практического смысла.

В этом случае мы можем прибегнуть к стандартному асинхронному методу платформы ПоказатьВыборЗначения(), предварительно выгрузив данные нашей ТЗ в объект СписокЗначений.

Давайте посмотрим на практический пример реализации такого механизма:


&НаКлиенте
Асинхронная Процедура ВыбратьИзПростогоСписка(Команда)

    // Шаг 1: Формируем список значений на сервере
    СписокДляВыбора = ПолучитьСписокДляВыбораНаСервере();
    
    // Шаг 2: Настраиваем оповещение
    ПараметрыОповещения = Новый ОписаниеОповещения("ПослеВыбораИзСписка", ЭтотОбъект);
    
    // Шаг 3: Показываем стандартный диалог выбора из списка асинхронно
    ПоказатьВыборЗначения(ПараметрыОповещения, СписокДляВыбора, "Выберите подходящий вариант:");

КонецПроцедуры

&НаСервере
Функция ПолучитьСписокДляВыбораНаСервере()

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

КонецФункции

&НаКлиенте
Процедура ПослеВыбораИзСписка(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт

    Если ВыбранныйЭлемент <> Неопределено Тогда
        // ВыбранныйЭлемент имеет тип ЭлементСпискаЗначений
        ЗначениеСсылка = ВыбранныйЭлемент.Значение;
        ПоказатьПредупреждение(, "Выбран элемент: " + Строка(ЗначениеСсылка));
    КонецЕсли;

КонецПроцедуры

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

---

Способ 3. Показ выбора в виде выпадающего меню (возле элемента формы)

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

Для этого в платформе предусмотрен метод ПоказатьВыборИзМеню(). Принцип его работы аналогичен работе со списком значений, но визуальное представление гораздо лаконичнее.

Рассмотрим по шагам реализацию этого интерфейсного решения:


&НаКлиенте
Процедура ПоказатьБыстроеМеню(Команда)

    // Локальный список вариантов для примера (можно получить с сервера)
    МенюВыбора = Новый СписокЗначений;
    МенюВыбора.Добавить("Вариант1", "Быстрый запуск");
    МенюВыбора.Добавить("Вариант2", "Полный анализ");
    МенюВыбора.Добавить("Вариант3", "Очистить результаты");
    
    // Настраиваем оповещение о закрытии меню
    ПараметрыОповещения = Новый ОписаниеОповещения("ПослеВыбораИзМеню", ЭтотОбъект);
    
    // Показываем меню. В качестве третьего параметра передаем элемент формы (кнопку),
    // к которой будет "привязано" выпадающее окошко.
    ЭлементКнопка = Элементы.КнопкаБыстрогоВыбора;
    ПоказатьВыборИзМеню(ПараметрыОповещения, МенюВыбора, ЭлементКнопка);

КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораИзМеню(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт

    Если ВыбранныйЭлемент <> Неопределено Тогда
        // Обрабатываем выбор пользователя
        ПоказатьПредупреждение(, "Выбрана команда: " + ВыбранныйЭлемент.Представление);
    КонецЕсли;

КонецПроцедуры

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

---

Резюме и рекомендации

Подведем краткие итоги анализа нашей проблемы:

Использование правильных паттернов клиент-серверного программирования гарантирует стабильную, быструю и отказоустойчивую работу ваших программных продуктов на платформе «1С:Предприятие 8.3».

← На главную