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

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

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

В этой статье мы подробно разберем, как правильно реализовать перемещение выделения, учитывая особенности платформы 1С 8.2 и 8.3, разницу между индексами и идентификаторами, а также влияние сортировки и фильтрации.

Понимаем разницу: Индекс строки vs Идентификатор строки

Первое, что нам необходимо проанализировать — это то, как управляемая форма идентифицирует строки в таблице. Многие начинающие разработчики путают индекс и идентификатор, что приводит к ошибкам при программном управлении ТекущаяСтрока.

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

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

Почему обычное сложение индексов не работает с отбором

Рассмотрим ситуацию: у нас есть таблица из 10 строк. Мы наложили отбор, и теперь в таблице видны только строки с индексами 2, 5 и 8. Если текущая строка имеет индекс 2, и мы программно скажем системе: «Перейди на индекс 3» (то есть 2 + 1), пользователь не увидит никаких изменений или курсор прыгнет в «пустоту», так как строка с индексом 3 скрыта фильтром. Наша задача — найти следующую видимую строку.

Использование метода ПроверитьСтроку()

Для решения этой проблемы в платформе 1С предусмотрен мощный инструмент — метод ПроверитьСтроку(). Рассмотрим его синтаксис и особенности:

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

Алгоритм «умного» перемещения курсора вниз

Разберем по шагам логику, которую нам необходимо реализовать для кнопки перемещения «Вниз»:

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

Проанализируем пример кода для реализации этой логики:


&НаКлиенте
Процедура ПереместитьВниз(Команда)
    
    ИмяТаблицы = "Номенклатура";
    ТаблицаЭлемент = Элементы[ИмяТаблицы];
    ДанныеКоллекции = Объект[ИмяТаблицы];
    
    // 1. Получаем идентификатор текущей строки
    ТекущийID = ТаблицаЭлемент.ТекущаяСтрока;
    Если ТекущийID = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // 2. Находим текущую строку и ее индекс
    ТекущаяСтрокаДанных = ДанныеКоллекции.НайтиПоИдентификатору(ТекущийID);
    ТекущийИндекс = ДанныеКоллекции.Индекс(ТекущаяСтрокаДанных);
    
    // 3. Перебор строк ниже текущей
    ВсегоСтрок = ДанныеКоллекции.Количество();
    Для Инд = ТекущийИндекс + 1 По ВсегоСтрок - 1 Цикл
        
        СтрокаПроверки = ДанныеКоллекции.Получить(Инд);
        IDПроверки = СтрокаПроверки.ПолучитьИдентификатор();
        
        // 4. Проверяем, видна ли строка с учетом отбора
        Если ТаблицаЭлемент.ПроверитьСтроку(IDПроверки) Тогда
            ТаблицаЭлемент.ТекущаяСтрока = IDПроверки;
            Прервать;
        КонецЕсли;
        
    КонецЦикла;

КонецПроцедуры

Алгоритм перемещения курсора вверх

Логика для перемещения вверх аналогична, но цикл будет идти в обратном направлении — от текущего индекса к началу коллекции (до 0).


&НаКлиенте
Процедура ПереместитьВверх(Команда)
    
    ИмяТаблицы = "Номенклатура";
    ТаблицаЭлемент = Элементы[ИмяТаблицы];
    ДанныеКоллекции = Объект[ИмяТаблицы];
    
    ТекущийID = ТаблицаЭлемент.ТекущаяСтрока;
    Если ТекущийID = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    ТекущаяСтрокаДанных = ДанныеКоллекции.НайтиПоИдентификатору(ТекущийID);
    ТекущийИндекс = ДанныеКоллекции.Индекс(ТекущаяСтрокаДанных);
    
    // Перебор строк выше текущей
    Для Инд = -(ТекущийИндекс - 1) По 0 Цикл
        // Используем шаг -1 через вычисление индекса
        РеальныйИндекс = -Инд; 
        
        СтрокаПроверки = ДанныеКоллекции.Получить(РеальныйИндекс);
        IDПроверки = СтрокаПроверки.ПолучитьИдентификатор();
        
        Если ТаблицаЭлемент.ПроверитьСтроку(IDПроверки) Тогда
            ТаблицаЭлемент.ТекущаяСтрока = IDПроверки;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    // Или более простой синтаксис цикла:
    // Для Инд = 1 По ТекущийИндекс Цикл
    //     ПроверяемыйИндекс = ТекущийИндекс - Инд;
    //     ...
    // КонецЦикла;

КонецПроцедуры

Влияние пользовательской сортировки

Разберем ситуацию, которая часто упускается из виду. Что, если в таблице нет отбора, но пользователь кликнул по заголовку колонки «Наименование», отсортировав строки по алфавиту? В этом случае визуальный порядок строк (то, что видит глаз) полностью перестает соответствовать индексам в коллекции ДанныеФормыКоллекция.

Если мы будем использовать вышеописанный код с перебором индексов, курсор начнет «прыгать» по таблице хаотично с точки зрения пользователя. К сожалению, объект ТаблицаФормы не предоставляет прямого доступа к «визуальному индексу» строки. Если нам критически важно поддерживать навигацию при включенной сортировке в обычной Таблице Значений, возможны два пути:

Пример использования имитации:


&НаКлиенте
Процедура ПереместитьВнизИмитация(Команда)
    Элементы.Номенклатура.ИзменитьТекущуюСтроку(НаправлениеПереходаКСтроке.Вниз);
КонецПроцедуры

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

Особенности синхронизации данных

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

Иногда требуется небольшая задержка или выполнение кода в отдельном потоке интерфейса. Если после Загрузить() выделение не устанавливается, попробуйте использовать ПодключитьОбработчикОжидания с минимальным интервалом (0.1 сек), чтобы дать платформе завершить отрисовку элементов и генерацию новых ID.

Резюме

Для надежного программного перемещения выделения в таблице на управляемых формах:

  1. Всегда работайте с Идентификаторами (через ПолучитьИдентификатор()), а не с индексами, когда дело касается свойства ТекущаяСтрока.
  2. Используйте метод ПроверитьСтроку() для фильтрации «невидимых» строк в цикле перебора.
  3. Учитывайте, что сортировка пользователем меняет визуальный порядок, который не совпадает с индексами коллекции. В таких случаях метод ИзменитьТекущуюСтроку() может оказаться более предпочтительным.
  4. Помните, что ПроверитьСтроку() — это клиентский метод, и вся логика навигации должна располагаться в клиентских процедурах.

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

← На главную