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