Курсор на нужную строку: управление текущей строкой таблицы формы из кода 1С 8.3
Содержание
В управляемой форме текущую строку табличной части устанавливают свойством элемента-таблицы: Элементы.Товары.ТекущаяСтрока = Строка.ПолучитьИдентификатор();. Присваивается не сама строка и не её номер, а идентификатор строки коллекции данных формы. В обычной форме всё проще: ЭлементыФормы.Товары.ТекущаяСтрока = СтрокаТЧ;, туда передаётся сам объект строки.
Код одинаково работает для табличной части документа, справочника, реквизита формы с типом ТаблицаЗначений и дерева значений. Ниже разобраны оба типа форм, переход на конкретную колонку, выделение нескольких строк и случаи, когда курсор «не слушается».
Почему присваивание не срабатывает с первого раза
Чаще всего код пишут по памяти из обычных форм и переносят в управляемую. Отсюда большинство сбоев. Курсор остаётся на месте или платформа выдаёт ошибку по одной из причин:
- 🔢 Передан номер строки, а не идентификатор.
НомерСтрокии индекс в коллекции не совпадают с идентификатором. После удаления и сортировки строк разница становится заметной сразу. - 📦 Передана сама строка. В управляемой форме объект
ДанныеФормыЭлементКоллекциивТекущаяСтрокане подходит, нужен результатПолучитьИдентификатор(). - 🖥️ Код выполнен не в том месте. Свойство меняют на клиенте или в серверной процедуре с контекстом формы. В
&НаСервереБезКонтекстаэлементов формы нет вообще. - 🔁 Коллекцию пересоздали. После
Объект.Товары.Загрузить(...)илиЗначениеВРеквизитФормыу всех строк новые идентификаторы. Старый идентификатор, сохранённый до этого, уже ни на что не указывает. - 🙈 Строка скрыта отбором. Если у таблицы задан
ОтборСтроки строка ему не соответствует, курсор на неё не встанет. - 🎯 Фокус на другом элементе. Строка формально стала текущей, но пользователь этого не видит: активным остаётся поле шапки.
Последний пункт — самый коварный. Ошибки нет, по ТекущиеДанные всё правильно, а глазами видна старая строка. Помогает отдельная установка фокуса, о ней ниже.
Три базовых приёма управления текущей строкой
Любая задача сводится к трём действиям: найти строку, сделать её текущей, при необходимости перевести на неё фокус и нужную колонку.
Найти строку
| Способ | Где работает | Когда использовать |
|---|---|---|
Объект.Товары[Индекс] | клиент и сервер | известен порядковый номер, индекс с нуля |
Объект.Товары.НайтиСтроки(Новый Структура("Номенклатура", Ссылка)) | клиент и сервер | поиск по значению одной или нескольких колонок |
Объект.Товары.НайтиПоИдентификатору(Ид) | клиент и сервер | идентификатор уже есть, например, вернулся с сервера |
Элементы.Товары.ТекущиеДанные | клиент | нужна строка, на которой сейчас стоит курсор |
НайтиСтроки() возвращает массив. Даже если подходящая строка одна, берите элемент [0] и проверяйте, что массив не пустой.
Сделать текущей
&НаКлиенте
Процедура УстановитьТекущуюСтроку(ИндексСтроки)
Если ИндексСтроки < 0 Или ИндексСтроки >= Объект.Товары.Количество() Тогда
Возврат;
КонецЕсли;
Строка = Объект.Товары[ИндексСтроки];
Элементы.Товары.ТекущаяСтрока = Строка.ПолучитьИдентификатор();
КонецПроцедуры
Индекс считается с нуля, а НомерСтроки — с единицы. Строке с номером 5 соответствует Объект.Товары[4].
Перевести фокус и колонку
ЭтотОбъект.ТекущийЭлемент = Элементы.Товары; // фокус на таблицу
Элементы.Товары.ТекущийЭлемент = Элементы.ТоварыКоличество; // активная колонка
ТекущийЭлемент у формы отвечает за то, какой элемент активен. ТекущийЭлемент у таблицы — за колонку внутри строки. Имя колонки берите из дерева элементов формы: обычно это имя таблицы плюс имя реквизита, но разработчик мог его переименовать.
Если после позиционирования нужно сразу открыть ячейку для ввода, вызовите Элементы.Товары.ИзменитьСтроку();. Пользователь увидит курсор в поле и сможет набирать число без двойного щелчка.
Управляемая форма: клиент, сервер и момент вызова
В управляемой форме данные таблицы живут в реквизите формы, а таблица на экране — лишь элемент, который к нему привязан. Поэтому строку ищут в Объект.Товары, а позицию задают у Элементы.Товары.
Когда строку добавляют на сервере
Типичный сценарий: строку заполняют на сервере (цены, остатки, расчёт), а курсор нужно поставить на неё после возврата на клиент. Надёжный путь — вернуть идентификатор из серверной функции и присвоить его на клиенте:
&НаКлиенте
Процедура ДобавитьУслугу(Команда)
ИдСтроки = ДобавитьУслугуНаСервере();
Элементы.Товары.ТекущаяСтрока = ИдСтроки;
ЭтотОбъект.ТекущийЭлемент = Элементы.Товары;
КонецПроцедуры
&НаСервере
Функция ДобавитьУслугуНаСервере()
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Количество = 1;
// заполнение цены, суммы и прочего
Возврат НоваяСтрока.ПолучитьИдентификатор();
КонецФункции
Идентификатор сохраняется при передаче данных формы между клиентом и сервером. Если же на сервере таблицу перезагрузили целиком через Загрузить(), ищите нужную строку заново уже после загрузки и возвращайте её новый идентификатор.
Где ставить курсор при открытии формы
Позиционирование «при открытии» надёжнее делать в обработчике ПриОткрытии на клиенте. Данные к этому моменту уже на клиенте, элемент таблицы создан. Если номер строки нужен с сервера, сохраните его в реквизит формы в ПриСозданииНаСервере, а в ПриОткрытии прочитайте и примените.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Параметры.Свойство("НомерСтроки", НомерСтрокиДляПерехода); // реквизит формы, тип Число
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Если НомерСтрокиДляПерехода > 0
И НомерСтрокиДляПерехода <= Объект.Товары.Количество() Тогда
Строка = Объект.Товары[НомерСтрокиДляПерехода - 1];
Элементы.Товары.ТекущаяСтрока = Строка.ПолучитьИдентификатор();
ЭтотОбъект.ТекущийЭлемент = Элементы.Товары;
КонецЕсли;
КонецПроцедуры
Выделение нескольких строк
Текущая строка всегда одна, а выделенных может быть много. Список выделенных хранится в свойстве ВыделенныеСтроки — это массив идентификаторов:
Элементы.Товары.ВыделенныеСтроки.Очистить();
Для Каждого Строка Из Объект.Товары.НайтиСтроки(Новый Структура("Количество", 0)) Цикл
Элементы.Товары.ВыделенныеСтроки.Добавить(Строка.ПолучитьИдентификатор());
КонецЦикла;
Для выделения нескольких строк у таблицы должен стоять режим выделения «Множественный» в свойствах элемента. Иначе пользователь увидит только одну строку.
Дерево и динамический список
Для дерева значений (ДанныеФормыДерево) правило то же: в ТекущаяСтрока передаётся идентификатор элемента. Если строка вложена, раскройте родителя методом Элементы.Дерево.Развернуть(ИдРодителя), иначе выделенный элемент останется внутри свёрнутой ветки.
У динамического списка коллекции строк на клиенте нет. Там ТекущаяСтрока принимает ссылку на объект (для справочника или документа) или ключ записи (для регистра).
Обычные формы: ТабличноеПоле и объект строки
В обычной форме (толстый клиент, конфигурации вроде УПП и БП 2.0) табличная часть показывается элементом ТабличноеПоле. Здесь ТекущаяСтрока принимает строку табличной части напрямую, идентификаторы не нужны:
Процедура ПерейтиКТовару(Номенклатура)
СтрокаТЧ = Товары.Найти(Номенклатура, "Номенклатура");
Если СтрокаТЧ = Неопределено Тогда
Возврат;
КонецЕсли;
ЭлементыФормы.Товары.ТекущаяСтрока = СтрокаТЧ;
ЭлементыФормы.Товары.ТекущаяКолонка = ЭлементыФормы.Товары.Колонки.Количество;
ТекущийЭлемент = ЭлементыФормы.Товары;
КонецПроцедуры
Чем обычная форма отличается от управляемой:
| Что делаем | Обычная форма | Управляемая форма |
|---|---|---|
| Доступ к элементу | ЭлементыФормы.Товары | Элементы.Товары |
Значение ТекущаяСтрока | строка табличной части | идентификатор строки |
| Активная колонка | ТекущаяКолонка | ТекущийЭлемент у таблицы |
| Данные текущей строки | ТекущиеДанные | ТекущиеДанные (только на клиенте) |
| Клиент и сервер | весь код в модуле формы | разделение &НаКлиенте / &НаСервере |
При переводе доработки с обычной формы на управляемую строку ТекущаяСтрока = СтрокаТЧ нельзя оставить как есть. Её нужно заменить на получение идентификатора.
Ошибки при установке текущей строки и как их снять
ЭтотОбъект.ТекущийЭлемент = Элементы.Товары. Без этого строка становится текущей, но на экране активно другое поле.Строка.ПолучитьИдентификатор().Загрузить(). Ищите строку заново после загрузки, например, через НайтиСтроки() по ключевым колонкам.&НаСервереБезКонтекста или в общем модуле. Перенесите присваивание в клиентскую процедуру формы, а с сервера возвращайте идентификатор.ОтборСтрок, и строка ему не соответствует. Снимите отбор: Элементы.Товары.ОтборСтрок = Неопределено — или поменяйте его условие.Если Элементы.Товары.ТекущиеДанные <> Неопределено Тогда.Отдельно про обработчик ПриАктивизацииСтроки. Если в нём есть логика (пересчёт подвала, загрузка связанных данных), она может отработать и после программной смены строки. Код, который ставит курсор в цикле, тогда вызывает обработчик лишний раз. Ставьте текущую строку один раз, в самом конце.
Готовые фрагменты кода под рабочие задачи
Сканер штрихкодов: найти товар или добавить, и показать строку
Самая частая задача. После сканирования курсор должен стоять на строке с этим товаром, чтобы кладовщик видел, что количество выросло.
&НаКлиенте
Процедура ОбработатьНоменклатуру(Номенклатура)
Найденные = Объект.Товары.НайтиСтроки(Новый Структура("Номенклатура", Номенклатура));
Если Найденные.Количество() > 0 Тогда
Строка = Найденные[0];
Строка.Количество = Строка.Количество + 1;
Иначе
Строка = Объект.Товары.Добавить();
Строка.Номенклатура = Номенклатура;
Строка.Количество = 1;
КонецЕсли;
Элементы.Товары.ТекущаяСтрока = Строка.ПолучитьИдентификатор();
ЭтотОбъект.ТекущийЭлемент = Элементы.Товары;
Модифицированность = Истина;
КонецПроцедуры
Цену и сумму в этом примере не пересчитываем: в типовых конфигурациях это делают отдельные процедуры модуля формы, их и вызывайте после изменения количества. Добавление строки на клиенте не взводит флаг изменения формы автоматически, поэтому Модифицированность выставляем явно.
Поставить курсор на последнюю строку после подбора
&НаКлиенте
Процедура ПерейтиНаПоследнююСтроку()
Количество = Объект.Товары.Количество();
Если Количество = 0 Тогда
Возврат;
КонецЕсли;
Элементы.Товары.ТекущаяСтрока = Объект.Товары[Количество - 1].ПолучитьИдентификатор();
КонецПроцедуры
Вызывайте её в конце ОбработкаВыбора, когда результат подбора уже перенесён в табличную часть.
Перейти к строке с ошибкой из сообщения
Сильный приём, о котором часто забывают. Не нужно вручную ставить курсор на строку с незаполненным полем: достаточно правильно указать поле в сообщении. Пользователь щёлкает по сообщению, и платформа сама переводит курсор в нужную ячейку.
Для Каждого Строка Из Объект.Товары Цикл
Если Строка.Количество = 0 Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Строка %1: не заполнено количество", Строка.НомерСтроки);
Сообщение.Поле = "Объект.Товары[" + Формат(Строка.НомерСтроки - 1, "ЧН=0; ЧГ=0") + "].Количество";
Сообщение.Сообщить();
КонецЕсли;
КонецЦикла;
В пути поля указывается индекс с нуля, отсюда НомерСтроки - 1. Формат с ЧН=0 нужен, чтобы для первой строки получился 0, а не пустая строка, а ЧГ=0 убирает пробел-разделитель в номерах от 1000.
В конфигурациях на БСП путь к ячейке удобнее собрать функцией ОбщегоНазначенияКлиентСервер.ПутьКТабличнойЧасти("Товары", НомерСтроки, "Количество") и передать в ОбщегоНазначения.СообщитьПользователю вместе с путём к данным "Объект". Функция принимает номер строки с единицы и сама пересчитывает его в индекс.
Производительность: когда документ на тысячи строк
На табличных частях в 50–100 строк скорость не видна. На документе с 3 000 строк разница между аккуратным и небрежным кодом — секунды на каждое действие.
- 🚫 Не вызывайте сервер в цикле. Каждый контекстный вызов передаёт на сервер все данные формы, включая всю табличную часть. Соберите изменения и сделайте один вызов.
- 🔍 Ищите через
НайтиСтроки(), а не перебором. Метод работает быстрее ручного цикла с условием и короче в коде. - 📌 Ставьте
ТекущаяСтрокаодин раз. Не перемещайте курсор на каждой итерации цикла: это лишние перерисовки и лишние срабатыванияПриАктивизацииСтроки. - 🧩 Тяжёлую логику в
ПриАктивизацииСтрокиоткладывайте. Подключите обработчик ожидания черезПодключитьОбработчикОжидания("ОбновитьПодвал", 0.2, Истина). Если пользователь листает строки стрелками, расчёт выполнится один раз, когда он остановится. - 🧱 Не перезагружайте таблицу целиком без нужды.
Загрузить()меняет все идентификаторы, и текущую строку приходится искать заново.
Если программная работа с табличной частью меняет данные документа, проверяйте результат на копии базы или в тестовом документе. Проведённый документ с переписанной табличной частью нужно перепровести, иначе движения по регистрам останутся старыми.
Частые вопросы
Можно ли установить текущую строку в процедуре &НаСервере?
Как узнать номер текущей строки в управляемой форме?
Элементы.Товары.ТекущиеДанные.НомерСтроки на клиенте. Перед этим проверьте, что ТекущиеДанные не равно Неопределено.Меняется ли идентификатор строки после записи документа?
Как сделать текущей строку в табличной части на вкладке, которая не открыта?
Элементы.Страницы.ТекущаяСтраница = Элементы.СтраницаТовары. Затем установите текущую строку и фокус на таблицу.