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

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

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

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

Суть проблемы: путаница между индексами и идентификаторами

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


СписокКонтрагентов = Новый СписокЗначений;
Для Каждого Стр Из Элементы.Контрагенты.ВыделенныеСтроки Цикл
    СписокКонтрагентов.Добавить(Объект.Контрагенты[Стр].Контрагент);
КонецЦикла;

На первый взгляд код выглядит логично. Однако при повторном открытии формы или после интерактивного удаления/добавления строк переменная Стр в цикле начинает принимать значения 5, 6, 7 и так далее, хотя физически в табличной части по-прежнему находится всего 4 строки. Попытка обратиться к строке коллекции по такому «индексу» вида Объект.Контрагенты[Стр] неизбежно приводит к аварийному завершению работы системы с ошибкой «Индекс находится за пределами границы».

Выясним причину такого поведения платформы. Ошибка кроется в неверном понимании природы данных, которые возвращает свойство ВыделенныеСтроки элемента формы «Таблица».

Разница между индексами и уникальными идентификаторами

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

  1. Индекс строки — это порядковый номер строки в коллекции (табличной части или таблице значений), который всегда начинается с 0 и последовательно увеличивается до Количество() - 1. Индексы жестко привязаны к физическому положению строк. Если мы удалим первую строку (с индексом 0), то бывшая вторая строка автоматически получит индекс 0.
  2. Идентификатор строки (ID) — это уникальное числовое значение, которое платформа присваивает строке табличного поля в момент её создания. Это значение предназначено для связи визуального элемента формы с данными объекта. Идентификатор никак не связан с порядковым номером строки в физическом массиве табличной части.

Рассмотрим важную особенность поведения идентификаторов: они никогда не сбрасываются и не пересчитываются в рамках текущего сеанса работы с формой. Если в таблице изначально было 4 строки, они получили идентификаторы от 0 до 3. Если пользователь полностью очистит табличную часть методом Объект.ТабличнаяЧасть.Очистить(), а затем добавит одну новую строку, то её индекс в коллекции будет равен 0, но её уникальный идентификатор на форме станет равен 4 (так как счетчик идентификаторов продолжил инкрементироваться).

Свойство Элементы.ИмяТаблицы.ВыделенныеСтроки возвращает массив именно идентификаторов строк, а не их индексов. Поэтому использовать эти значения для прямого обращения к элементам коллекции через квадратные скобки категорически нельзя.

Решение №1: Использование метода НайтиПоИдентификатору()

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

Давайте перепишем код с использованием безопасного поиска по идентификатору. Рассмотрим готовый пример процедуры:


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

Обратите внимание на обязательную проверку СтрокаТЧ <> Неопределено. Разберем по шагам, почему она критически важна. В процессе интерактивной работы пользователя (например, в обработчиках событий «ПередУдалением» или при программном манипулировании данными) в массиве выделенных строк на долю секунды могут оставаться идентификаторы строк, которые уже физически удалены из коллекции. Если пренебречь проверкой на Неопределено, обращение к несуществующей строке вызовет критическую ошибку выполнения кода.

Решение №2: Использование метода ДанныеСтроки() элемента формы

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

Посмотрим на пример его реализации:


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

Данный метод имеет как очевидные преимущества, так и скрытые нюансы, которые необходимо учитывать при проектировании архитектуры вашего решения.

Преимущества метода ДанныеСтроки():

Важное ограничение метода ДанныеСтроки():

Этот метод возвращает только те данные колонок, которые физически доступны на клиенте в данный момент времени. Если в вашей табличной части есть служебный реквизит, который не выведен на форму в виде колонки, получить его значение через ДанныеСтроки() на клиенте не удастся. Для обхода этого ограничения необходимо либо переносить обработку на сервер и использовать первый метод через НайтиПоИдентификатору(), либо в свойствах пути к данным этого реквизита на форме установить флаг «Использовать всегда».

Особенности работы с Динамическими списками

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

В этом случае логика работы платформы кардинально меняется:

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


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

Как мы видим, в случае динамического списка метод ДанныеСтроки() в качестве параметра принимает саму ссылку на объект, что делает работу со списками чрезвычайно простой и лаконичной.

Работа с Деревом значений и интерактивными методами

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

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


&НаКлиенте
Процедура РазвернутьВыделенныеВеткиДерева()
    
    Для Каждого Идентификатор Из Элементы.ДеревоНоменклатуры.ВыделенныеСтроки Цикл
        // Программный разворот узла дерева по его уникальному ID
        Элементы.ДеревоНоменклатуры.Развернуть(Идентификатор, Истина);
    КонецЦикла;
    
КонецПроцедуры

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

Подведем итоги нашего совместного разбора темы работы с выделенными строками на управляемых формах 1С:

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

← На главную