Всегда ли Индекс строки табличной части равен НомерСтроки - 1 в 1С:Предприятие 8.3 (Управляемые формы)?

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

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

Исходная проблема: предположение о соответствии Индекс = НомерСтроки - 1

Как часто бывает, мы можем предположить, что при обращении к строке табличной части по индексу, он будет равен значению стандартного реквизита "Номер строки" минус единица. Например, если мы имеем данные, полученные из запроса, где есть поле "НомерСтроки", мы можем попытаться обратиться к строке табличной части формы следующим образом:


Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий Цикл
    // Мы предполагаем, что Индекс = НомерСтроки - 1
    Форма.Объект.Товары[Выборка.НомерСтроки - 1].СерийныйНомер = СокрЛП(Выборка.САПКод);
КонецЦикла;

Но действительно ли это предположение всегда верно? Разберем эту ситуацию.

Различия между НомерСтроки, Индекс, Идентификатор и ИсходныйНомерСтроки

Для начала, давайте определим ключевые понятия, чтобы устранить любую путаницу.

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

Когда Индекс не равен НомерСтроки - 1: анализ поведения

Ответ на вопрос "всегда ли Индекс равен НомерСтроки - 1?" — нет, не всегда. Это соответствие справедливо только для табличных частей, которые были только что прочитаны из базы данных или только что созданы, и порядок строк в которых не менялся ни программно, ни интерактивно.

Рассмотрим ситуации, когда это соответствие нарушается:

  1. Перемещение строк: Если пользователь перетаскивает строки в табличной части вверх или вниз, Индекс каждой перемещенной строки изменится, так как изменится ее позиция в коллекции. При этом реквизит НомерСтроки в данных формы может остаться прежним до записи, или же его значение будет изменено платформой визуально.
  2. Удаление строк: При удалении строки, все строки, расположенные после удаленной, "сдвигаются" вверх, и их Индексы меняются. НомерСтроки также может быть перестроен.
  3. Добавление строк: При добавлении новой строки, ей присваивается следующий доступный индекс. НомерСтроки также будет автоматически присвоен.
  4. Сортировка: Если пользователь применяет сортировку к табличной части, Индексы строк полностью перестроятся в соответствии с новым порядком.

Для демонстрации этого поведения, мы можем провести эксперимент, используя следующий код на клиенте, который выводит Индекс, НомерСтроки и Идентификатор при активизации строки:


&НаКлиенте
Процедура ТабличнаяЧасть1ПриАктивизацииСтроки(Элемент)
    Если Объект.ТабличнаяЧасть1.Индекс(Элемент.ТекущиеДанные) > -1 Тогда
        Сообщить("Индекс: " + Объект.ТабличнаяЧасть1.Индекс(Элемент.ТекущиеДанные)
                 + " Номер строки: " + Элемент.ТекущиеДанные.НомерСтроки
                 + " Идентификатор строки: " + Элементы.ТабличнаяЧасть1.ТекущаяСтрока);
    КонецЕсли;
КонецПроцедуры

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

Как надежно работать со строками табличной части

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

Сценарий 1: Обновление существующих строк

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

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

Рассмотрим пример кода, демонстрирующий, как получить строку данных по ее идентификатору, который можно получить из элемента формы:


Идентификатор = Элементы.ТаблицаФормы.ТекущаяСтрока; // Получаем идентификатор текущей строки элемента формы

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

        // Если все же нужны Индекс и НомерСтроки для каких-то целей,
        // можно получить их от найденной СтрокаДанных
        ИндексВКоллекции = ДанныеТаблицыФормы.Индекс(СтрокаДанных);
        ТекущийНомерСтроки = ИндексВКоллекции + 1; // Это будет текущий номер строки на форме
        ИсходныйНомерСтрокиСтроки = СтрокаДанных.ИсходныйНомерСтроки; // Номер строки из базы на момент чтения

        Сообщить("Обновлена строка. Индекс в коллекции: " + ИндексВКоллекции
                 + ", Текущий номер строки на форме: " + ТекущийНомерСтроки
                 + ", Исходный номер строки из БД: " + ИсходныйНомерСтрокиСтроки);
    Иначе
        Сообщить("Строка данных не найдена по идентификатору.");
    КонецЕсли;
Иначе
    Сообщить("Идентификатор текущей строки не определен (например, это может быть строка группировки).");
КонецЕсли;

Сценарий 2: Заполнение табличной части из запроса, когда порядок не важен

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

  1. Очистить табличную часть: Сначала полностью очищаем табличную часть на форме. Объект.ИмяТабличнойЧасти.Очистить();
  2. Заполнить новыми строками: Затем в цикле добавляем новые строки и заполняем их данными из запроса. В этом случае, Индекс будет последовательным, а НомерСтроки заполнится автоматически.
    
    ВыборкаДанных = Запрос.Выполнить().Выбрать();
    Пока ВыборкаДанных.Следующий() Цикл
        НоваяСтрока = Объект.ИмяТабличнойЧасти.Добавить();
        НоваяСтрока.Реквизит1 = ВыборкаДанных.Поле1;
        НоваяСтрока.Реквизит2 = ВыборкаДанных.Поле2;
        // И так далее для других реквизитов
    КонецЦикла;
    

Сценарий 3: Заполнение табличной части с сохранением порядка из базы данных

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

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

  1. Перебрать строки из нашей выборки.
  2. Для каждой строки выборки попытаться найти соответствующую строку в табличной части формы по какому-либо уникальному реквизиту (например, код номенклатуры).
  3. Если строка найдена, обновить ее. Если не найдена, добавить новую.
  4. После обновления всех строк из выборки, пройтись по строкам табличной части формы и удалить те, которые не были обновлены (то есть, которых нет в нашей выборке).
  5. Наконец, отсортировать табличную часть формы по НомерСтроки или по ИсходныйНомерСтроки если это необходимо для визуального представления, чтобы привести ее в порядок, соответствующий данным из БД.

Заключение

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

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

← На главную