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

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

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

Почему сбрасывается текущая строка? Разберемся в причинах

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

Когда мы вызываем метод Очистить() или полностью заменяем таблицу через Загрузить(), платформа удаляет все старые строки вместе с их идентификаторами. Даже если новые данные абсолютно идентичны старым, им присваиваются новые идентификаторы. В результате визуальный элемент «теряет» старую привязку и по умолчанию позиционируется на первую доступную строку. Наша задача — научить систему находить «ту самую» строку после обновления и программно возвращать на неё фокус.

Метод 1: Сохранение позиции по уникальному ключу

Этот способ считается самым надежным. Если в вашей таблице есть колонка с уникальными данными (например, Ссылка, Артикул или внутренний ИдентификаторСтроки), мы можем использовать её для восстановления позиции. Рассмотрим алгоритм действий по шагам.

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


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

// 2. Перезаполняем таблицу
ОбновитьДанныеТаблицы(); 

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

Важный нюанс: при использовании этого метода важно помнить, что поиск строк в ДанныеФормыКоллекция работает аналогично поиску в ТаблицеЗначений, но результат всегда возвращается в виде массива. Не забудьте проверить количество найденных элементов, прежде чем обращаться к индексу [0].

Метод 2: «Мягкое» обновление данных (без очистки)

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

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


// На сервере
ТекИндекс = -1;
КоличествоСтарыхСтрок = Объект.ДанныеТаблица.Количество();

Для каждого СтрокаИзИсточника Из ВнешнийИсточник Цикл
    ТекИндекс = ТекИндекс + 1;
    
    // Если строка по такому индексу уже существует - редактируем её
    Если ТекИндекс < КоличествоСтарыхСтрок Тогда
        ТекущаяСтрокаФормы = Объект.ДанныеТаблица[ТекИндекс];
    Иначе
        // Если строк в источнике больше, чем было - добавляем новые
        ТекущаяСтрокаФормы = Объект.ДанныеТаблица.Добавить();
    КонецЕсли;
    
    ЗаполнитьЗначенияСвойств(ТекущаяСтрокаФормы, СтрокаИзИсточника);
КонецЦикла;

// Удаляем лишние строки, если старая таблица была длиннее новой
Пока Объект.ДанныеТаблица.Количество() > ТекИндекс + 1 Цикл
    Объект.ДанныеТаблица.Удалить(Объект.ДанныеТаблица.Количество() - 1);
КонецЦикла;

Этот подход гарантирует, что если пользователь стоял на 5-й строке, и после обновления в таблице всё еще есть 5 и более строк, фокус останется на месте. Идентификаторы первых строк не изменятся, так как мы не вызывали Очистить().

Метод 3: Смещение фокуса на следующую строку (как в динамическом списке)

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

  1. Перед обновлением сохраняем индекс текущей строки через Объект.Таблица.Индекс(Строка).
  2. Выполняем обновление данных.
  3. Проверяем количество строк в новой таблице.
  4. Если строк стало меньше, и наш старый индекс теперь недоступен, выбираем индекс последней строки новой таблицы.
  5. Устанавливаем фокус, используя метод ПолучитьИдентификатор().

Посмотрим на практическую реализацию этой логики:


// Запоминаем индекс перед действием
ИндексДоОбновления = Объект.МояТаблица.Индекс(Объект.МояТаблица.НайтиПоИдентификатору(Элементы.МояТаблица.ТекущаяСтрока));

// Выполняем заполнение/обновление
ОбновитьДанные();

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

Тонкости работы с идентификаторами и типами данных

Выясним одну важную деталь, на которую указывали участники обсуждения. При работе с методом НайтиПоИдентификатору мы получаем объект типа ДанныеФормыСтруктура (или ДанныеФормыЭлементКоллекции). Это не то же самое, что СтрокаТаблицыЗначений.

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

Помните: работа с индексами (Объект.Таблица[НужныйИндекс]) эффективна только в том случае, если в таблице на форме не включена пользовательская сортировка. Если пользователь кликнул по заголовку колонки (или было применено программное перемещение колонки) и отсортировал данные, визуальный порядок строк перестанет совпадать с порядком строк в коллекции данных. В этой ситуации единственным стабильным решением остается поиск по ключевому полю (ID записи).

Подведем итоги

Для качественной реализации сохранения позиции курсора мы рекомендуем:

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

← На главную