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

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

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

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

Выясняем причину проблемы: разница между индексом и идентификатором

Для начала проанализируем стандартную ошибку, которую часто совершают разработчики, привыкшие к синтаксису обычных форм (1С 8.2 и 7.7). На управляемой форме таблица на самом деле представлена объектом типа ДанныеФормыКоллекция, а элемент отображения на форме — это ТаблицаФормы.

Рассмотрим неработающий код, который приводит к путанице с индексами:


// ОШИБОЧНЫЙ КОД!
СтрокаТаб = ТаблицаДокументов.Получить(Элементы.ТаблицаДокументов.ТекущаяСтрока);
ТаблицаДокументов.Удалить(Элементы.ТаблицаДокументов.ТекущаяСтрока);

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

Метод же коллекции Получить(Индекс) ожидает на входе именно индекс строки (число от 0 до N-1). Когда мы только открыли форму, идентификаторы строк и их индексы могут случайно совпадать (например, первая строка имеет индекс 0 и идентификатор 0, вторая — индекс 1 и идентификатор 1). Но как только мы удаляем, к примеру, самую первую строку:

  1. Физический индекс второй строки автоматически уменьшается и становится равным 0 (происходит смещение всей коллекции вверх).
  2. При этом уникальный идентификатор этой строки остается прежним (он равен 1).
  3. Если после этого мы попытаемся обратиться к текущей строке через Получить(ТекущаяСтрока), система попытается получить строку по индексу 1, хотя фактически эта строка теперь находится по индексу 0. В результате мы считываем данные совершенно другой строки или получаем ошибку обращения к несуществующему элементу.

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

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

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

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


&НаКлиенте
Процедура УдалитьТекущуюСтрокуИПолучитьДанные()
    
    // Получаем идентификатор текущей строки на форме
    ИдентификаторСтроки = Элементы.ТаблицаДокументов.ТекущаяСтрока;
    
    Если ИдентификаторСтроки <> Неопределено Тогда
        
        // 1. Находим саму строку в коллекции по идентификатору
        ТекущаяСтрокаДанных = ТаблицаДокументов.НайтиПоИдентификатору(ИдентификаторСтроки);
        
        Если ТекущаяСтрокаДанных <> Неопределено Тогда
            
            // Получаем необходимые данные перед удалением
            ДокументДляОткрытия = ТекущаяСтрокаДанных.ДокументПогрузки;
            
            // 2. Безопасно удаляем строку из коллекции
            ТаблицаДокументов.Удалить(ТекущаяСтрокаДанных);
            
            // Теперь индексы пересчитались, но это не повлияет на дальнейшую работу
            ПоказатьПредупреждение(, "Удален документ: " + СокрЛП(ДокументДляОткрытия));
            
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

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

Решение №2. Использование свойства ТекущиеДанные (Наиболее простой и лаконичный способ)

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

Посмотрим на пример реализации удаления через ТекущиеДанные:


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

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

Решение №3. Безопасное удаление нескольких выделенных строк (Мультивыбор)

Довольно часто перед разработчиком стоит задача реализовать удаление не одной текущей строки, а сразу нескольких строк, которые пользователь выделил в списке (с зажатой клавишей Ctrl или Shift). В этом случае свойство ТекущиеДанные вернет только одну активную строку, а для работы со всем списком выделений нам понадобится свойство ВыделенныеСтроки.

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

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


&НаКлиенте
Процедура УдалитьВыделенныеСтроки()
    
    СписокИдентификаторов = Элементы.ТаблицаДокументов.ВыделенныеСтроки;
    
    // Создаем временный массив для хранения непосредственных объектов строк
    МассивСтрокДляУдаления = Новый Массив;
    
    // Шаг 1. Собираем ссылки на объекты строк по их идентификаторам
    Для Каждого Идентификатор Из СписокИдентификаторов Цикл
        СтрокаКоллекции = ТаблицаДокументов.НайтиПоИдентификатору(Идентификатор);
        Если СтрокаКоллекции <> Неопределено Тогда
            МассивСтрокДляУдаления.Добавить(СтрокаКоллекции);
        КонецЕсли;
    КонецЦикла;
    
    // Шаг 2. Безопасно удаляем элементы из исходной коллекции документов
    Для Каждого СтрокаИзСписка Из МассивСтрокДляУдаления Цикл
        ТаблицаДокументов.Удалить(СтрокаИзСписка);
    КонецЦикла;
    
КонецПроцедуры

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

Важные подводные камни при работе со строками управляемых форм

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

  1. Сброс идентификаторов при серверных вызовах. Вы должны помнить, что идентификаторы строк таблицы гарантированно стабильны только во время работы на клиенте. Если в процессе работы происходит вызов серверной процедуры, в которой данные формы конвертируются в прикладные объекты (например, при помощи вызова РеквизитФормыВЗначение("Объект")) и затем возвращаются обратно в форму (ЗначениеВРеквизитФормы()), платформа полностью пересоздает коллекцию данных формы. При этом все старые идентификаторы строк уничтожаются, и генерируются новые. Никогда не сохраняйте идентификаторы строк в глобальные переменные модуля формы для использования после вызова сервера! Для связи строк после серверных контекстных вызовов используйте уникальные бизнес-ключи (например, реквизит "КлючСвязи" или Уникальный Идентификатор ссылки).
  2. Влияние события ПриАктивизацииСтроки. При программном удалении строки из таблицы платформа автоматически позиционирует курсор на следующую за ней строку (или на предыдущую, если была удалена последняя строка списка). Это действие неизбежно вызывает системное событие таблицы формы ПриАктивизацииСтроки. Если внутри обработчика этого события у вас написан ресурсоемкий код (например, выполняются тяжелые запросы к серверу для отображения связанных остатков или цен), удаление строки может вызвать зависание интерфейса. При написании обработчиков активизации всегда предусматривайте быструю проверку на равенство текущих данных значению Неопределено.

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

← На главную