Почему параметр ВыбраннаяСтрока в таблице формы не равен индексу строки и как получить данные после очистки таблицы

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

При разработке управляемых форм в платформе «1С:Предприятие 8.3» разработчики нередко сталкиваются с ситуацией: после программной очистки табличной части и последующего добавления новых строк при двойном клике по строке в событии Выбор параметр ВыбраннаяСтрока содержит не ожидаемый индекс 0, 1, 2..., а число, увеличенное на количество ранее удаленных строк. Попытка обратиться к коллекции формы по такому индексу вида Объект.ТаблицаСоответствий[ВыбраннаяСтрока] приводит к ошибке выхода индекса за границы диапазона.

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

В чем разница между идентификатором строки, индексом и номером строки

Проанализируем терминологию платформы «1С:Предприятие», так как непонимание различий между этими понятиями чаще всего порождает ошибки:

  1. Идентификатор строки — это уникальное целочисленное число, которое платформа автоматически присваивает строке коллекции данных формы (объект ДанныеФормыКоллекция) в момент ее создания. Внутренний счетчик идентификаторов действует на протяжении всего жизненного цикла формы. Метод Очистить() удаляет элементы из коллекции, но не сбрасывает счетчик автоинкремента. Параметр ВыбраннаяСтрока в обработчиках событий таблицы (например, Выбор или ПриАктивизацииСтроки) возвращает именно идентификатор строки, а не ее порядковый номер.
  2. Индекс строки — это порядковое смещение элемента в коллекции, всегда начинающееся с нуля (0) и заканчивающееся значением Количество() - 1. К элементам коллекции через оператор квадратных скобок [] можно обращаться исключительно по индексу.
  3. Номер строки — это номер строки, отображаемый пользователю в интерфейсе (свойство НомерСтроки), нумерация которого начинается с единицы (1). Справедливо соотношение: Индекс = НомерСтроки - 1.

Посмотрим на пример ошибочного кода, в котором идентификатор строки ошибочно используется как индекс массива:


&НаКлиенте
Процедура ТаблицаСоответствийВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    // ОШИБКА: ВыбраннаяСтрока передает идентификатор, а не индекс!
    сЗапуститьПриложение(ВыбраннаяСтрока);
КонецПроцедуры

&НаСервере
Процедура сЗапуститьПриложение(ВыбраннаяСтрока)
    // При ВыбраннаяСтрока >= Количество() произойдет аварийное завершение
    Строка = Объект.ТаблицаСоответствий[ВыбраннаяСтрока];
    ПолноеИмяФайла = Строка.ПолноеИмяФайла;
    // ...
КонецПроцедуры

Способ 1. Получение данных текущей строки на клиенте через свойство ТекущиеДанные

Рассмотрим самый простой и эффективный способ. Если действие пользователя (например, запуск файла, открытие формы или расчет) инициируется интерактивно на клиенте, обращаться к серверу только ради чтения данных строки нет необходимости. Элемент формы таблицы предоставляет свойство ТекущиеДанные, содержащее объект ДанныеФормыЭлементКоллекции.

Посмотрим на корректную реализацию клиентского обработчика:


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

Обратите внимание: вызов метода ЗапуститьПриложение на стороне сервера запускает процесс в контексте системной службы сервера «1С:Предприятия», а не на компьютере текущего пользователя. Все операции взаимодействия с локальными файлами пользователя и их открытие должны выполняться строго в директиве &НаКлиенте.

Способ 2. Поиск строки по идентификатору методом НайтиПоИдентификатору

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

Разберем программную реализацию:


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

Способ 3. Получение реального индекса строки в коллекции

Если в дальнейшем алгоритме требуется именно целочисленный индекс строки (например, для позиционирования или работы с коллекциями), его можно вычислить с помощью метода Индекс() коллекции формы, передав в него полученный элемент строки:


&НаКлиенте
Процедура ОбработатьСтроку(ВыбраннаяСтрока)
    СтрокаТаблицы = Объект.ТаблицаСоответствий.НайтиПоИдентификатору(ВыбраннаяСтрока);
    Если СтрокаТаблицы <> Неопределено Тогда
        ИндексСтроки = Объект.ТаблицаСоответствий.Индекс(СтрокаТаблицы);
        // Теперь ИндексСтроки содержит значение от 0 до Количество() - 1
    КонецЕсли;
КонецПроцедуры

Рекомендации по оптимизации работы с промежуточными данными

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

Разберем правила оптимизации таких алгоритмов:

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