При разработке интерфейсов на управляемых формах 1С разработчики часто сталкиваются с неожиданным поведением: после программного удаления строки из таблицы при последующем клике или выборе строки обработчики событий возвращают данные совершенно другой строки (обычно со смещением на одну позицию ниже). Разберем причины такого поведения, выясним технические особенности работы платформы с коллекциями данных формы и рассмотрим правильные способы получения и удаления строк.
Рассмотрим классический пример, в котором возникает ошибка. На управляемой форме расположена таблица, связанная с реквизитом типа ТаблицаЗначений. В коде формы реализована команда удаления строки и обработчик события выбора строки таблицы:
&НаКлиенте
Процедура УдалитьСтроку(Команда)
ИндексСтроки = Элементы.СписокСловДляКонтроля.ТекущаяСтрока;
СписокСловДляКонтроля.Удалить(ИндексСтроки);
КонецПроцедуры
&НаКлиенте
Процедура СписокСловДляКонтроляВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
сСписокСловДляКонтроля = СписокСловДляКонтроля[ВыбраннаяСтрока];
текПеревод = ПолучитьПеревод(сСписокСловДляКонтроля.Слово);
ПоказатьПредупреждение(, текПеревод);
КонецПроцедуры
Проанализируем, что происходит при выполнении этого кода:
Выясним причину этой ошибки: разработчик ошибочно принимает значение ТекущаяСтрока и параметр ВыбраннаяСтрока за порядковый индекс строки в таблице значений.
На клиенте табличный реквизит формы представлен типом ДанныеФормыКоллекция, а строки — типом ДанныеФормыЭлементКоллекции. Принципиальные различия между индексом и идентификатором заключаются в следующем:
0 до Количество() - 1. При удалении строки из середины коллекции индексы всех последующих строк уменьшаются на единицу. Обращение через квадратные скобки Коллекция[Индекс] ожидает именно порядковый индекс.ТекущаяСтрока, ВыделенныеСтроки, а также параметр ВыбраннаяСтрока в событиях Выбор, ОбработкаВыбора и ПриАктивизацииСтроки возвращают идентификатор строки, а не ее индекс.В нашем примере, когда в таблице было 5 строк, им были присвоены идентификаторы 1, 2, 3, 4, 5 (или с 0 по 4 в зависимости от инициализации). После удаления строки с идентификатором 2 оставшиеся строки сохранили свои идентификаторы. При выборе третьей строки параметр ВыбраннаяСтрока вернул идентификатор 3. Обращение СписокСловДляКонтроля[3] выполнило поиск по индексу 3 (а это 4-я по порядку строка в текущей усеченной коллекции), из-за чего и возникло смещение данных.
Рассмотрим базовый и наиболее надежный способ получения строки коллекции по ее идентификатору. Для этого у объекта ДанныеФормыКоллекция предназначен метод НайтиПоИдентификатору().
&НаКлиенте
Процедура СписокСловДляКонтроляВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
СтрокаДанных = СписокСловДляКонтроля.НайтиПоИдентификатору(ВыбраннаяСтрока);
Если СтрокаДанных <> Неопределено Тогда
текПеревод = ПолучитьПеревод(СтрокаДанных.Слово);
ПоказатьПредупреждение(, текПеревод);
КонецЕсли;
КонецПроцедуры
Разберем также правильное удаление строки по ее идентификатору. Метод Удалить() у коллекции данных формы умеет принимать в качестве параметра сам объект строки:
&НаКлиенте
Процедура УдалитьСтроку(Команда)
ИдентификаторСтроки = Элементы.СписокСловДляКонтроля.ТекущаяСтрока;
Если ИдентификаторСтроки <> Неопределено Тогда
СтрокаДляУдаления = СписокСловДляКонтроля.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаДляУдаления <> Неопределено Тогда
СписокСловДляКонтроля.Удалить(СтрокаДляУдаления);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если требуется обработать строку, на которой в данный момент установлен курсор, нет необходимости запрашивать идентификатор и затем выполнять поиск. Посмотрим на свойство ТекущиеДанные элемента таблицы формы:
&НаКлиенте
Процедура УдалитьТекущуюСтроку(Команда)
ТекущаяСтрокаДанных = Элементы.СписокСловДляКонтроля.ТекущиеДанные;
Если ТекущаяСтрокаДанных <> Неопределено Тогда
СписокСловДляКонтроля.Удалить(ТекущаяСтрокаДанных);
КонецЕсли;
КонецПроцедуры
Важно: свойство ТекущиеДанные возвращает объект строки ДанныеФормыЭлементКоллекции. Обязательно проверяйте результат на Неопределено, так как при пустой таблице или отсутствии фокуса на строке свойство не инициализировано.
Рассмотрим ситуацию, когда в таблице включен множественный выбор и пользователю необходимо удалить или изменить сразу несколько выделенных строк. Свойство ВыделенныеСтроки содержит массив идентификаторов. Так как удаление строк не изменяет идентификаторы оставшихся элементов, мы можем безопасно обходить массив без пересчета позиций:
&НаКлиенте
Процедура УдалитьВыделенныеСтроки(Команда)
Для Каждого ИдСтроки Из Элементы.СписокСловДляКонтроля.ВыделенныеСтроки Цикл
СтрокаТаблицы = СписокСловДляКонтроля.НайтиПоИдентификатору(ИдСтроки);
Если СтрокаТаблицы <> Неопределено Тогда
СписокСловДляКонтроля.Удалить(СтрокаТаблицы);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Если в алгоритме требуется узнать именно индекс строки в таблице (например, для вычисления взаимного расположения элементов), используем метод Индекс() коллекции формы, предварительно получив саму строку по идентификатору:
&НаКлиенте
Процедура ОпределитьИндексСтроки(Идентификатор)
СтрокаДанных = СписокСловДляКонтроля.НайтиПоИдентификатору(Идентификатор);
Если СтрокаДанных <> Неопределено Тогда
ПорядковыйИндекс = СписокСловДляКонтроля.Индекс(СтрокаДанных);
Сообщить("Порядковый индекс строки: " + ПорядковыйИндекс);
КонецЕсли;
КонецПроцедуры
Помните, что при перезаполнении таблицы методами ЗначениеВРеквизитФормы() или Коллекция.Загрузить() платформа генерирует новые идентификаторы для всех строк, поэтому сохранять идентификаторы между серверными вызовами с полной перезагрузкой данных формы нельзя.