Как программно перейти на следующую строку в динамическом списке 1С

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

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

В этой статье мы подробно разберем несколько подходов к решению данной задачи: от простых «хаков» с эмуляцией клавиатуры до серьезных методов анализа данных на стороне сервера.

Особенности динамического списка и проблема идентификации

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

Главная сложность заключается в том, что «следующая» строка определяется текущей сортировкой списка. Если пользователь изменил порядок сортировки в форме, «следующим» элементом станет совершенно другой объект. Кроме того, из-за механизма «ленивой загрузки» клиентское приложение может просто «не знать» о существовании следующей строки, если она еще не была подгружена с сервера в видимую область.

Способ №1: Использование эмуляции нажатия клавиш (SendKeys)

Это самый популярный и простой в реализации метод, предложенный участниками обсуждения. Суть заключается в программном вызове команды «Стрелка вниз», как если бы её нажал сам пользователь. Рассмотрим, как это реализовать правильно с использованием обработчика ожидания.

Нам понадобится COM-объект Wscript.Shell. Важно понимать, что прямое выполнение команды в событии ПриИзменении может не сработать, так как фокус ввода в этот момент может быть еще не возвращен основному списку (например, если закрывается форма редактирования). Поэтому мы используем ПодключитьОбработчикОжидания.

Разберем пример кода для управляемой формы:


&НаКлиенте
Процедура СписокПриИзменении(Элемент)
    // Запускаем кратковременный обработчик, чтобы дать форме завершить текущие процессы
    ПодключитьОбработчикОжидания("ВыполнитьПереходВниз", 0.1, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьПереходВниз()
    // Проверяем, доступен ли ввод в текущей форме
    Если НЕ ЭтаФорма.ВводДоступен() Тогда
        // Если форма еще занята, пробуем снова через 0.1 сек
        ПодключитьОбработчикОжидания("ВыполнитьПереходВниз", 0.1, Истина);
        Возврат;
    КонецЕсли;

    // Пытаемся создать объект для отправки клавиш
    Попытка
        Shell = Новый COMОбъект("Wscript.Shell");
        Shell.SendKeys("{DOWN}");
    Исключение
        // Метод работает только в ОС Windows и только в тонком/толстом клиенте
        Сообщить("Не удалось выполнить автоматический переход: " + ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

Преимущества этого метода:

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

Недостатки:

Способ №2: Определение ссылки на следующую строку через запрос

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

Для этого проанализируем алгоритм:

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

Посмотрим на упрощенный пример реализации на сервере:


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

После получения ссылки на клиенте достаточно написать:


СледующаяСсылка = ПолучитьСледующуюСсылку(Элементы.Список.ТекущаяСтрока, "Список");
Если ЗначениеЗаполнено(СледующаяСсылка) Тогда
    Элементы.Список.ТекущаяСтрока = СледующаяСсылка;
КонецЕсли;

Важный момент: Использование ПолучитьИсполняемуюСхемуКомпоновкиДанных() эффективно, но при огромных объемах данных в динамическом списке (десятки тысяч строк) полная выгрузка в ТаблицуЗначений может вызвать зависание интерфейса. В таких случаях лучше использовать прямой запрос к БД с условием ВЫБРАТЬ ПЕРВЫЕ 1, где в секции ГДЕ прописаны условия, соответствующие текущей сортировке (например, Наименование > &ТекущееНаименование).

Способ №3: Использование произвольного запроса с нумерацией

Как было подмечено в сообщении №8 обсуждения, если динамический список построен на произвольном запросе и не имеет основной таблицы, идентификатором строки (ТекущаяСтрока) может выступать обычное число. Это справедливо для списков, где данные нумеруются внутри запроса. Для изучения различных подходов к изменению структуры запроса и полей списка можно ознакомиться с руководством по методам доработки динамических списков.

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


&НаКлиенте
Процедура СписокПриИзменении(Элемент)
    ТекущийИндекс = Элементы.Список.ТекущаяСтрока;
    Если ТипЗнч(ТекущийИндекс) = Тип("Число") Тогда
        Элементы.Список.ТекущаяСтрока = ТекущийИндекс + 1;
    КонецЕсли;
КонецПроцедуры

Однако помните, что это частный случай. Для стандартных списков справочников или документов, где ключом является Ссылка, этот метод неприменим напрямую.

Резюме и рекомендации

Подведем итог нашему исследованию. Выбор метода зависит от ваших требований к надежности и окружению:

  1. Если ваша база работает только в тонком клиенте под Windows, используйте Способ №1 (SendKeys). Это обеспечит максимально естественное поведение формы с минимальными затратами ресурсов сервера.
  2. Если важна кроссплатформенность и работа в браузере, используйте Способ №2. При этом старайтесь оптимизировать запрос к серверу, чтобы не выгружать лишние данные.
  3. Всегда учитывайте настройки сортировки. Если пользователь отсортировал список вручную, программный расчет «следующей» строки должен обязательно это учитывать, иначе курсор будет прыгать хаотично, что только запутает пользователя.

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

← На главную