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

Программист
← На главную

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

Почему стандартный цикл «Для каждого» не работает?

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

Как отмечают участники обсуждения, проблема «стара как мир»: при удалении текущего элемента выборка сбивается. Чтобы обойти это ограничение, нам нужно использовать алгоритмы, не зависящие от динамического изменения порядка элементов в исходном массиве.

Метод 1: Обратный цикл (от конца к началу)

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

Рассмотрим пример кода, реализующий этот подход:


&НаКлиенте
Процедура УдалитьВыделенныеСтроки(Команда)
    
    // Получаем массив идентификаторов выделенных строк
    МассивИдентификаторов = Элементы.ТабличнаяЧасть1.ВыделенныеСтроки;
    Количество = МассивИдентификаторов.Количество();
    
    // Обходим массив с конца к началу
    Для Индекс = 1 По Количество Цикл
        
        // Вычисляем обратный индекс
        ТекущийИдентификатор = МассивИдентификаторов[Количество - Индекс];
        
        // Находим строку в данных объекта по её уникальному идентификатору
        НайденнаяСтрока = Объект.ТабличнаяЧасть1.НайтиПоИдентификатору(ТекущийИдентификатор);
        
        Если НайденнаяСтрока <> Неопределено Тогда
            Объект.ТабличнаяЧасть1.Удалить(НайденнаяСтрока);
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

В данном примере мы используем метод НайтиПоИдентификатору, так как свойство ВыделенныеСтроки содержит именно уникальные идентификаторы, а не порядковые номера строк. Это критически важно для управляемых форм.

Метод 2: Цикл «Пока» и удаление первого элемента

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

Проанализируем пример кода:


МассивУдаляемыхСтрок = Элементы.ТабличнаяЧасть1.ВыделенныеСтроки;
КоличествоСтрок = МассивУдаляемыхСтрок.Количество();

Для Сч = 1 По КоличествоСтрок Цикл
    // Всегда берем элемент с индексом 0
    Идентификатор = МассивУдаляемыхСтрок[0];
    НайденнаяСтрока = Объект.ТабличнаяЧасть1.НайтиПоИдентификатору(Идентификатор);
    
    Если НайденнаяСтрока <> Неопределено Тогда
        Объект.ТабличнаяЧасть1.Удалить(НайденнаяСтрока);
    КонецЕсли;
КонецЦикла;

Этот метод элегантен тем, что нам не нужно заботиться о сортировке или сложных математических расчетах индексов. Мы просто «вычищаем» массив с головы.

Метод 3: Использование списка значений и сортировки

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

Разберем, как это работает:

  1. Копируем идентификаторы из массива в СписокЗначений.
  2. Сортируем список по убыванию.
  3. Удаляем строки, основываясь на отсортированных данных.

М = Элементы.ТабличнаяЧасть.ВыделенныеСтроки;
СЗ = Новый СписокЗначений;
СЗ.ЗагрузитьЗначения(М);

// Сортируем, чтобы удалять строки в правильном порядке
СЗ.СортироватьПоЗначению(НаправлениеСортировки.Убыв);

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

Важные технические нюансы

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

  1. Различие между Идентификатором и Индексом: В управляемых формах ВыделенныеСтроки содержат идентификаторы типа Число. Метод таблицы Удалить() может принимать как индекс строки, так и саму строку (объект). Мы рекомендуем сначала получать объект строки через НайтиПоИдентификатору(), а затем передавать этот объект в метод Удалить().
  2. Проверка на Неопределено: Всегда проверяйте результат поиска строки. Если в процессе работы формы произойдет какое-то фоновое событие или программное изменение данных, строка может исчезнуть до того, как ваш цикл до нее доберется. Попытка удалить Неопределено вызовет фатальную ошибку.
  3. Производительность: Если таблица содержит десятки тысяч строк, и пользователь выделяет значительную их часть, удаление в цикле на клиенте может привести к «зависанию» интерфейса. В таких случаях рассмотрим перенос логики на сервер. На сервере можно создать массив удаляемых объектов, передать его в серверную процедуру и выполнить массовое удаление там, что значительно быстрее.
  4. Обновление интерфейса: После массового удаления строк платформа обычно сама обновляет связанные элементы формы. Однако, если вы работаете с реквизитом формы типа ТаблицаЗначений, который не связан с данными объекта, может потребоваться принудительное обновление фокуса, чтобы курсор не «повис» в пустом пространстве.

Рекомендованный универсальный код

На основе анализа всех предложенных вариантов, мы можем составить наиболее стабильный вариант функции, который подойдет для большинства задач в 1С 8.3:


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

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

← На главную