Позиционирование на нужной записи в динамическом списке управляемой формы

· 9 мин

Содержание

Чтобы установить текущую строку динамического списка 1С, присвойте ключ нужной записи свойству ТекущаяСтрока элемента формы: Элементы.Список.ТекущаяСтрока = Ссылка;. Для списка справочника или документа ключ — это ссылка на объект. Для списка регистра сведений — ключ записи, который создают методом СоздатьКлючЗаписи().

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

Что хранит динамический список и почему в нём нет «номера строки»

Динамический список не держит данные в памяти формы. Он читает из базы порцию строк, которая видна на экране, и дочитывает следующие при прокрутке. Поэтому индексов вида Список[5] у него нет, а коллекции строк на клиенте тоже нет.

Каждая строка опознаётся ключом. От основной таблицы зависит, что это за ключ:

Основная таблица спискаТип ключа строкиЧем установить
Справочник, документ, план видов характеристик, бизнес-процессСсылка на объектЭлементы.Список.ТекущаяСтрока = Ссылка
Регистр сведенийРегистрСведенийКлючЗаписи.<Имя>Ключ из СоздатьКлючЗаписи()
Произвольный запрос без основной таблицыКлюч по ключевым полям спискаКлюч, ранее полученный из ТекущаяСтрока

Три свойства табличного элемента формы закрывают почти все задачи:

  • 📍 ТекущаяСтрока — ключ строки под курсором. Читается и записывается.
  • 📄 ТекущиеДанные — значения колонок текущей строки. Только чтение.
  • ✅ ВыделенныеСтроки — массив ключей выделенных строк. Его можно очистить и заполнить.

Не путайте реквизит формы Список и элемент Элементы.Список. Текущая строка — свойство элемента. У реквизита такого свойства нет, и строка Список.ТекущаяСтрока = ... даст ошибку.

Установка текущей строки по ссылке на объект

Самый частый случай: форма списка справочника или документа, и после какого-то действия курсор нужно поставить на конкретный элемент.

Пример: найти элемент и встать на него

&НаКлиенте
Процедура ПерейтиКТовару(Команда)

	Ссылка = НайтиТоварПоАртикулу(Артикул);

	Если Не ЗначениеЗаполнено(Ссылка) Тогда
		ПоказатьПредупреждение(, "Товар с таким артикулом не найден");
		Возврат;
	КонецЕсли;

	Элементы.Список.ТекущаяСтрока = Ссылка;
	ТекущийЭлемент = Элементы.Список;

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

&НаСервереБезКонтекста
Функция НайтиТоварПоАртикулу(Знач Артикул)

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	Номенклатура.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	Номенклатура.Артикул = &Артикул";
	Запрос.УстановитьПараметр("Артикул", Артикул);

	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Выборка.Ссылка;
	КонецЕсли;

	Возврат Справочники.Номенклатура.ПустаяСсылка();

КонецФункции

Поиск вынесен в функцию &НаСервереБезКонтекста: так на сервер не уходят данные формы. Строка ТекущийЭлемент = Элементы.Список переводит фокус в таблицу. Без неё курсор встанет, но пользователь продолжит работать с полем ввода артикула.

Текущая строка при открытии формы

Если форму списка или выбора открывают из кода, передайте стандартный параметр ТекущаяСтрока. Платформа сама поставит курсор на переданный элемент:

ПараметрыФормы = Новый Структура("ТекущаяСтрока", Ссылка);
ОткрытьФорму("Справочник.Номенклатура.ФормаСписка", ПараметрыФормы);

В своей форме, где список — обычный реквизит, позиционируйте в ПриСозданииНаСервере:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

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

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

Здесь Документ — параметр, который вы добавили форме сами в конфигураторе.

Как получить ссылку текущей строки

Обратная задача — прочитать, на чём стоит курсор:

&НаКлиенте
Процедура ПоказатьТекущий(Команда)

	ТекущиеДанные = Элементы.Список.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат; // список пуст или курсор на строке группировки
	КонецЕсли;

	Ссылка = ТекущиеДанные.Ссылка;
	Наименование = ТекущиеДанные.Наименование;
	Сообщить(Наименование);

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

Для списка объектов Элементы.Список.ТекущаяСтрока уже возвращает ссылку. Через ТекущиеДанные удобно брать значения колонок, которые выведены в список, без лишнего обращения к серверу.

Выделение нескольких строк

Если режим выделения у таблицы множественный, кроме курсора можно задать набор выделенных строк:

Элементы.Список.ВыделенныеСтроки.Очистить();
Для Каждого Ссылка Из МассивСсылок Цикл
	Элементы.Список.ВыделенныеСтроки.Добавить(Ссылка);
КонецЦикла;
Элементы.Список.ТекущаяСтрока = МассивСсылок[0];

Курсор и выделение — разные вещи. Выделить строки без курсора можно, но пользователь увидит рамку там, где курсор стоял раньше. Поэтому последнюю строку кода не пропускайте.

Установка текущей строки через ключ записи регистра

В списке регистра сведений ссылки нет. Строка опознаётся набором измерений, а у периодического регистра — ещё и периодом. Ключ собирают из структуры:

&НаСервере
Процедура ВстатьНаЦену(Номенклатура, ВидЦены, Период)

	КлючевыеЗначения = Новый Структура;
	КлючевыеЗначения.Вставить("Период", Период);
	КлючевыеЗначения.Вставить("Номенклатура", Номенклатура);
	КлючевыеЗначения.Вставить("ВидЦены", ВидЦены);

	Ключ = РегистрыСведений.ЦеныНоменклатуры.СоздатьКлючЗаписи(КлючевыеЗначения);
	Элементы.Список.ТекущаяСтрока = Ключ;

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

Имена в структуре должны совпадать с именами измерений регистра. Метод СоздатьКлючЗаписи() доступен на сервере, поэтому процедура помечена &НаСервере.

Три правила, на которых чаще всего ошибаются:

  1. Передавайте все измерения. Пропущенное измерение примет пустое значение, и ключ укажет на запись, которой нет.
  2. Период — с точностью до периодичности регистра. У регистра с периодичностью «Месяц» в ключе должно стоять начало месяца: НачалоМесяца(Дата), а не произвольная дата внутри месяца.
  3. Ключ записи читается. Из ТекущаяСтрока списка регистра можно взять отдельные значения: Элементы.Список.ТекущаяСтрока.Номенклатура.

Список на произвольном запросе

Если у динамического списка не задана основная таблица, строки различаются по ключевым полям, которые указывают в свойствах списка. Конструировать такой ключ вручную неудобно. Рабочий приём — запомнить ключ из самого списка и потом вернуть его:

&НаКлиенте
Перем СохраненныйКлюч;

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

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

Так восстанавливают позицию после смены отбора или после обновления, которое вы вызвали сами.

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

Текущая строка в иерархическом списке и в дереве

У справочника с иерархией есть три режима отображения: плоский список, иерархический список и дерево. Режим задаётся свойством таблицы Отображение, и от него зависит, увидит ли пользователь нужный элемент сразу.

РежимЧто показывает таблицаЧто сделать при позиционировании
СписокВсе элементы одним уровнемДостаточно ТекущаяСтрока
Иерархический списокСодержимое одной группыЗадать ТекущийРодитель, затем ТекущаяСтрока
ДеревоГруппы с раскрытиемТекущаяСтрока, при необходимости Развернуть()

Иерархический список

В этом режиме таблица показывает один уровень. Чтобы перейти к элементу внутри группы, сначала откройте его группу, потом поставьте курсор:

&НаКлиенте
Процедура ПерейтиВГруппуИВстать(Ссылка, Родитель)

	Элементы.Список.ТекущийРодитель = Родитель;
	Элементы.Список.ТекущаяСтрока = Ссылка;

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

Если форма открывается сразу на нужном элементе, то же самое делают в ПриСозданииНаСервере: сначала ТекущийРодитель, затем ТекущаяСтрока. Порядок важен. Если сначала поставить строку, а потом сменить родителя, таблица покажет другую группу, и курсор окажется на её первой строке.

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

Дерево

В режиме дерева группу можно раскрыть методом Развернуть(). Второй параметр отвечает за раскрытие всех подчинённых уровней:

Элементы.Список.Развернуть(Группа, Ложь);
Элементы.Список.ТекущаяСтрока = Ссылка;

Раскрывать дерево целиком (Истина во втором параметре) на большом справочнике не стоит. Платформа прочитает все уровни, и форма заметно задумается.

Отбор по родителю

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

Почему курсор не встаёт на нужную строку

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

Диагностика: выберите симптом

Пустые и отсутствующие данные

Перед присваиванием проверьте три вещи:

  • 🔍 Значение заполнено. Пустая ссылка в ТекущаяСтрока ошибки не даст, но и смысла в ней нет.
  • 🗂️ Тип совпадает с основной таблицей. Если форма универсальная и принимает ссылки разных типов, сравните тип: ТипЗнч(Ссылка) = Тип("СправочникСсылка.Номенклатура").
  • 🧹 Запись проходит отборы списка. Если отбор важен для работы пользователя, лучше показать сообщение «запись скрыта отбором», чем молча снимать его.

Порядок действий после записи

Когда пользователь создаёт элемент через стандартную команду формы списка, платформа сама ставит курсор на новую строку. Ручное позиционирование нужно, если объект создаётся из кода или в другой форме. В таком случае:

  1. Запишите объект и получите его ссылку.
  2. Вызовите ОповеститьОбИзменении(Ссылка) — список перечитается.
  3. Поставьте ТекущаяСтрока = Ссылка.

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

Скорость: как позиционировать и не тормозить форму

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

Что замедляет позиционирование

  • 🐢 Сортировка по неиндексированному полю. Чтобы найти соседей строки, платформа сортирует выборку. Сортировка по реквизиту без индекса на большой таблице читает её целиком. Включите для реквизита индексирование или сортируйте по полю с индексом.
  • 🐢 Тяжёлый обработчик ПриПолученииДанныхНаСервере. Он срабатывает на каждую прочитанную порцию. Запрос внутри цикла по строкам умножает время на размер порции.
  • 🐢 Вычисляемые поля в запросе списка. Подзапросы и соединения с регистрами остатков выполняются на каждое чтение.
  • 🐢 Позиционирование в цикле. Перебор строк через ТекущаяСтрока ради поиска — это десятки обращений к серверу. Ищите запросом, а ставьте курсор один раз.

Что помогает

  • ⚡ Свойство списка «Динамическое считывание данных» оставляйте включённым для больших таблиц: список читает только видимую часть.
  • ⚡ Ищите нужную ссылку в функции &НаСервереБезКонтекста и передавайте на клиент только результат.
  • ⚡ Если нужен родитель для иерархического списка, получайте его тем же запросом.
  • ⚡ На клиенте читайте значения через ТекущиеДанные, а не через ОбщегоНазначения.ЗначениеРеквизитаОбъекта() на каждую смену строки в ПриАктивизацииСтроки.

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

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
	ОтключитьОбработчикОжидания("ОбновитьПанельСведений");
	ПодключитьОбработчикОжидания("ОбновитьПанельСведений", 0.2, Истина);
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьПанельСведений()
	ТекущиеДанные = Элементы.Список.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;
	ЗаполнитьСведенияНаСервере(ТекущиеДанные.Ссылка);
КонецПроцедуры

Процедуру ЗаполнитьСведенияНаСервере напишите под свою форму: она получает ссылку и заполняет реквизиты панели.

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

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