Курсор на нужную строку: управление текущей строкой таблицы формы из кода 1С 8.3

· 9 мин

Содержание

В управляемой форме текущую строку табличной части устанавливают свойством элемента-таблицы: Элементы.Товары.ТекущаяСтрока = Строка.ПолучитьИдентификатор();. Присваивается не сама строка и не её номер, а идентификатор строки коллекции данных формы. В обычной форме всё проще: ЭлементыФормы.Товары.ТекущаяСтрока = СтрокаТЧ;, туда передаётся сам объект строки.

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

Почему присваивание не срабатывает с первого раза

Чаще всего код пишут по памяти из обычных форм и переносят в управляемую. Отсюда большинство сбоев. Курсор остаётся на месте или платформа выдаёт ошибку по одной из причин:

  • 🔢 Передан номер строки, а не идентификатор. НомерСтроки и индекс в коллекции не совпадают с идентификатором. После удаления и сортировки строк разница становится заметной сразу.
  • 📦 Передана сама строка. В управляемой форме объект ДанныеФормыЭлементКоллекции в ТекущаяСтрока не подходит, нужен результат ПолучитьИдентификатор().
  • 🖥️ Код выполнен не в том месте. Свойство меняют на клиенте или в серверной процедуре с контекстом формы. В &НаСервереБезКонтекста элементов формы нет вообще.
  • 🔁 Коллекцию пересоздали. После Объект.Товары.Загрузить(...) или ЗначениеВРеквизитФормы у всех строк новые идентификаторы. Старый идентификатор, сохранённый до этого, уже ни на что не указывает.
  • 🙈 Строка скрыта отбором. Если у таблицы задан ОтборСтрок и строка ему не соответствует, курсор на неё не встанет.
  • 🎯 Фокус на другом элементе. Строка формально стала текущей, но пользователь этого не видит: активным остаётся поле шапки.

Последний пункт — самый коварный. Ошибки нет, по ТекущиеДанные всё правильно, а глазами видна старая строка. Помогает отдельная установка фокуса, о ней ниже.

Три базовых приёма управления текущей строкой

Любая задача сводится к трём действиям: найти строку, сделать её текущей, при необходимости перевести на неё фокус и нужную колонку.

Найти строку

СпособГде работаетКогда использовать
Объект.Товары[Индекс]клиент и серверизвестен порядковый номер, индекс с нуля
Объект.Товары.НайтиСтроки(Новый Структура("Номенклатура", Ссылка))клиент и серверпоиск по значению одной или нескольких колонок
Объект.Товары.НайтиПоИдентификатору(Ид)клиент и серверидентификатор уже есть, например, вернулся с сервера
Элементы.Товары.ТекущиеДанныеклиентнужна строка, на которой сейчас стоит курсор

НайтиСтроки() возвращает массив. Даже если подходящая строка одна, берите элемент [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, Истина). Если пользователь листает строки стрелками, расчёт выполнится один раз, когда он остановится.
  • 🧱 Не перезагружайте таблицу целиком без нужды. Загрузить() меняет все идентификаторы, и текущую строку приходится искать заново.

Если программная работа с табличной частью меняет данные документа, проверяйте результат на копии базы или в тестовом документе. Проведённый документ с переписанной табличной частью нужно перепровести, иначе движения по регистрам останутся старыми.

Частые вопросы

Можно ли установить текущую строку в процедуре &НаСервере?
Присвоить свойство элементу формы в серверной процедуре с контекстом можно. Но надёжнее вернуть идентификатор на клиент и присвоить там: так не возникает расхождений, если таблицу перезагружают в том же вызове.
Как узнать номер текущей строки в управляемой форме?
Возьмите Элементы.Товары.ТекущиеДанные.НомерСтроки на клиенте. Перед этим проверьте, что ТекущиеДанные не равно Неопределено.
Меняется ли идентификатор строки после записи документа?
При записи форма перечитывает данные объекта, и идентификаторы строк могут смениться. Если после записи нужно вернуться к той же строке, запомните её номер или значение ключевой колонки и найдите строку заново.
Как сделать текущей строку в табличной части на вкладке, которая не открыта?
Сначала переключите страницу: Элементы.Страницы.ТекущаяСтраница = Элементы.СтраницаТовары. Затем установите текущую строку и фокус на таблицу.
Работает ли ТекущаяСтрока в веб-клиенте и мобильном клиенте?
Да, свойство относится к элементу управляемой формы и работает во всех клиентах. Отличаться может только отрисовка, а не сам механизм.