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