Позиционирование на нужной записи в динамическом списке управляемой формы
Содержание
Чтобы установить текущую строку динамического списка 1С, присвойте ключ нужной записи свойству ТекущаяСтрока элемента формы: Элементы.Список.ТекущаяСтрока = Ссылка;. Для списка справочника или документа ключ — это ссылка на объект. Для списка регистра сведений — ключ записи, который создают методом СоздатьКлючЗаписи().
Способ выбирают по основной таблице списка. Есть основная таблица-объект — хватает ссылки. Основная таблица — регистр, нужен ключ записи. Произвольный запрос без основной таблицы — ключ строки собирается из ключевых полей, и надёжнее всего брать его у самого списка. Всё это работает в управляемых формах платформы 8.3 и одинаково в любой конфигурации.
Что хранит динамический список и почему в нём нет «номера строки»
Динамический список не держит данные в памяти формы. Он читает из базы порцию строк, которая видна на экране, и дочитывает следующие при прокрутке. Поэтому индексов вида Список[5] у него нет, а коллекции строк на клиенте тоже нет.
Каждая строка опознаётся ключом. От основной таблицы зависит, что это за ключ:
| Основная таблица списка | Тип ключа строки | Чем установить |
|---|---|---|
| Справочник, документ, план видов характеристик, бизнес-процесс | Ссылка на объект | Элементы.Список.ТекущаяСтрока = Ссылка |
| Регистр сведений | РегистрСведенийКлючЗаписи.<Имя> | Ключ из СоздатьКлючЗаписи() |
| Произвольный запрос без основной таблицы | Ключ по ключевым полям списка | Ключ, ранее полученный из ТекущаяСтрока |
Три свойства табличного элемента формы закрывают почти все задачи:
- 📍
ТекущаяСтрока— ключ строки под курсором. Читается и записывается. - 📄
ТекущиеДанные— значения колонок текущей строки. Только чтение. - ✅
ВыделенныеСтроки— массив ключей выделенных строк. Его можно очистить и заполнить.
Не путайте реквизит формы Список и элемент Элементы.Список. Текущая строка — свойство элемента. У реквизита такого свойства нет, и строка Список.ТекущаяСтрока = ... даст ошибку.
Установка текущей строки по ссылке на объект
Самый частый случай: форма списка справочника или документа, и после какого-то действия курсор нужно поставить на конкретный элемент.
Пример: найти элемент и встать на него
&НаКлиенте
Процедура ПерейтиКТовару(Команда)
Ссылка = НайтиТоварПоАртикулу(Артикул);
Если Не ЗначениеЗаполнено(Ссылка) Тогда
ПоказатьПредупреждение(, "Товар с таким артикулом не найден");
Возврат;
КонецЕсли;
Элементы.Список.ТекущаяСтрока = Ссылка;
ТекущийЭлемент = Элементы.Список;
КонецПроцедуры
&НаСервереБезКонтекста
Функция НайтиТоварПоАртикулу(Знач Артикул)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Артикул = &Артикул";
Запрос.УстановитьПараметр("Артикул", Артикул);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Ссылка;
КонецЕсли;
Возврат Справочники.Номенклатура.ПустаяСсылка();
КонецФункции
Поиск вынесен в функцию &НаСервереБезКонтекста: так на сервер не уходят данные формы. Строка ТекущийЭлемент = Элементы.Список переводит фокус в таблицу. Без неё курсор встанет, но пользователь продолжит работать с полем ввода артикула.
Текущая строка при открытии формы
Если форму списка или выбора открывают из кода, передайте стандартный параметр ТекущаяСтрока. Платформа сама поставит курсор на переданный элемент:
ПараметрыФормы = Новый Структура("ТекущаяСтрока", Ссылка);
ОткрытьФорму("Справочник.Номенклатура.ФормаСписка", ПараметрыФормы);
В своей форме, где список — обычный реквизит, позиционируйте в ПриСозданииНаСервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если ЗначениеЗаполнено(Параметры.Документ) Тогда
Элементы.Список.ТекущаяСтрока = Параметры.Документ;
КонецЕсли;
КонецПроцедуры
Здесь Документ — параметр, который вы добавили форме сами в конфигураторе.
Как получить ссылку текущей строки
Обратная задача — прочитать, на чём стоит курсор:
&НаКлиенте
Процедура ПоказатьТекущий(Команда)
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат; // список пуст или курсор на строке группировки
КонецЕсли;
Ссылка = ТекущиеДанные.Ссылка;
Наименование = ТекущиеДанные.Наименование;
Сообщить(Наименование);
КонецПроцедуры
Для списка объектов Элементы.Список.ТекущаяСтрока уже возвращает ссылку. Через ТекущиеДанные удобно брать значения колонок, которые выведены в список, без лишнего обращения к серверу.
Выделение нескольких строк
Если режим выделения у таблицы множественный, кроме курсора можно задать набор выделенных строк:
Элементы.Список.ВыделенныеСтроки.Очистить();
Для Каждого Ссылка Из МассивСсылок Цикл
Элементы.Список.ВыделенныеСтроки.Добавить(Ссылка);
КонецЦикла;
Элементы.Список.ТекущаяСтрока = МассивСсылок[0];
Курсор и выделение — разные вещи. Выделить строки без курсора можно, но пользователь увидит рамку там, где курсор стоял раньше. Поэтому последнюю строку кода не пропускайте.
Установка текущей строки через ключ записи регистра
В списке регистра сведений ссылки нет. Строка опознаётся набором измерений, а у периодического регистра — ещё и периодом. Ключ собирают из структуры:
&НаСервере
Процедура ВстатьНаЦену(Номенклатура, ВидЦены, Период)
КлючевыеЗначения = Новый Структура;
КлючевыеЗначения.Вставить("Период", Период);
КлючевыеЗначения.Вставить("Номенклатура", Номенклатура);
КлючевыеЗначения.Вставить("ВидЦены", ВидЦены);
Ключ = РегистрыСведений.ЦеныНоменклатуры.СоздатьКлючЗаписи(КлючевыеЗначения);
Элементы.Список.ТекущаяСтрока = Ключ;
КонецПроцедуры
Имена в структуре должны совпадать с именами измерений регистра. Метод СоздатьКлючЗаписи() доступен на сервере, поэтому процедура помечена &НаСервере.
Три правила, на которых чаще всего ошибаются:
- Передавайте все измерения. Пропущенное измерение примет пустое значение, и ключ укажет на запись, которой нет.
- Период — с точностью до периодичности регистра. У регистра с периодичностью «Месяц» в ключе должно стоять начало месяца:
НачалоМесяца(Дата), а не произвольная дата внутри месяца. - Ключ записи читается. Из
ТекущаяСтрокасписка регистра можно взять отдельные значения:Элементы.Список.ТекущаяСтрока.Номенклатура.
Список на произвольном запросе
Если у динамического списка не задана основная таблица, строки различаются по ключевым полям, которые указывают в свойствах списка. Конструировать такой ключ вручную неудобно. Рабочий приём — запомнить ключ из самого списка и потом вернуть его:
&НаКлиенте
Перем СохраненныйКлюч;
&НаКлиенте
Процедура ЗапомнитьПозицию()
СохраненныйКлюч = Элементы.Список.ТекущаяСтрока;
КонецПроцедуры
&НаКлиенте
Процедура ВосстановитьПозицию()
Если СохраненныйКлюч <> Неопределено Тогда
Элементы.Список.ТекущаяСтрока = СохраненныйКлюч;
КонецЕсли;
КонецПроцедуры
Так восстанавливают позицию после смены отбора или после обновления, которое вы вызвали сами.
Ключевые поля должны однозначно определять строку. Если по выбранным полям в результате запроса окажутся две одинаковые строки, курсор встанет не туда или прыгнет при прокрутке.
Текущая строка в иерархическом списке и в дереве
У справочника с иерархией есть три режима отображения: плоский список, иерархический список и дерево. Режим задаётся свойством таблицы Отображение, и от него зависит, увидит ли пользователь нужный элемент сразу.
| Режим | Что показывает таблица | Что сделать при позиционировании |
|---|---|---|
| Список | Все элементы одним уровнем | Достаточно ТекущаяСтрока |
| Иерархический список | Содержимое одной группы | Задать ТекущийРодитель, затем ТекущаяСтрока |
| Дерево | Группы с раскрытием | ТекущаяСтрока, при необходимости Развернуть() |
Иерархический список
В этом режиме таблица показывает один уровень. Чтобы перейти к элементу внутри группы, сначала откройте его группу, потом поставьте курсор:
&НаКлиенте
Процедура ПерейтиВГруппуИВстать(Ссылка, Родитель)
Элементы.Список.ТекущийРодитель = Родитель;
Элементы.Список.ТекущаяСтрока = Ссылка;
КонецПроцедуры
Если форма открывается сразу на нужном элементе, то же самое делают в ПриСозданииНаСервере: сначала ТекущийРодитель, затем ТекущаяСтрока. Порядок важен. Если сначала поставить строку, а потом сменить родителя, таблица покажет другую группу, и курсор окажется на её первой строке.
Родителя получите на сервере вместе со ссылкой, одним вызовом. Если запрашивать его отдельной функцией, а ссылку — другой, получится два обращения к серверу вместо одного.
Дерево
В режиме дерева группу можно раскрыть методом Развернуть(). Второй параметр отвечает за раскрытие всех подчинённых уровней:
Элементы.Список.Развернуть(Группа, Ложь);
Элементы.Список.ТекущаяСтрока = Ссылка;
Раскрывать дерево целиком (Истина во втором параметре) на большом справочнике не стоит. Платформа прочитает все уровни, и форма заметно задумается.
Отбор по родителю
Частая ловушка — отбор Родитель в настройках списка. Если элемент лежит в другой группе, отбор его скроет, и никакое позиционирование не поможет. Сначала снимите или поменяйте отбор, потом ставьте курсор.
Почему курсор не встаёт на нужную строку
Платформа не выдаёт исключение, если ключ не найден среди видимых строк. Курсор просто остаётся на месте. Поэтому ошибку приходится искать по симптомам.
Пустые и отсутствующие данные
Перед присваиванием проверьте три вещи:
- 🔍 Значение заполнено. Пустая ссылка в
ТекущаяСтрокаошибки не даст, но и смысла в ней нет. - 🗂️ Тип совпадает с основной таблицей. Если форма универсальная и принимает ссылки разных типов, сравните тип:
ТипЗнч(Ссылка) = Тип("СправочникСсылка.Номенклатура"). - 🧹 Запись проходит отборы списка. Если отбор важен для работы пользователя, лучше показать сообщение «запись скрыта отбором», чем молча снимать его.
Порядок действий после записи
Когда пользователь создаёт элемент через стандартную команду формы списка, платформа сама ставит курсор на новую строку. Ручное позиционирование нужно, если объект создаётся из кода или в другой форме. В таком случае:
- Запишите объект и получите его ссылку.
- Вызовите
ОповеститьОбИзменении(Ссылка)— список перечитается. - Поставьте
ТекущаяСтрока = Ссылка.
Если поменять местами второй и третий шаги, новой записи ещё не будет среди прочитанных строк.
Скорость: как позиционировать и не тормозить форму
Установка текущей строки заставляет список прочитать порцию данных вокруг нужной записи. Обычно это быстро, но несколько вещей превращают операцию в заметную паузу.
Что замедляет позиционирование
- 🐢 Сортировка по неиндексированному полю. Чтобы найти соседей строки, платформа сортирует выборку. Сортировка по реквизиту без индекса на большой таблице читает её целиком. Включите для реквизита индексирование или сортируйте по полю с индексом.
- 🐢 Тяжёлый обработчик
ПриПолученииДанныхНаСервере. Он срабатывает на каждую прочитанную порцию. Запрос внутри цикла по строкам умножает время на размер порции. - 🐢 Вычисляемые поля в запросе списка. Подзапросы и соединения с регистрами остатков выполняются на каждое чтение.
- 🐢 Позиционирование в цикле. Перебор строк через
ТекущаяСтрокаради поиска — это десятки обращений к серверу. Ищите запросом, а ставьте курсор один раз.
Что помогает
- ⚡ Свойство списка «Динамическое считывание данных» оставляйте включённым для больших таблиц: список читает только видимую часть.
- ⚡ Ищите нужную ссылку в функции
&НаСервереБезКонтекстаи передавайте на клиент только результат. - ⚡ Если нужен родитель для иерархического списка, получайте его тем же запросом.
- ⚡ На клиенте читайте значения через
ТекущиеДанные, а не черезОбщегоНазначения.ЗначениеРеквизитаОбъекта()на каждую смену строки вПриАктивизацииСтроки.
Обработчик ПриАктивизацииСтроки срабатывает при каждом переходе курсора, в том числе при программной установке текущей строки. Если в нём есть серверный вызов, оберните его в ПодключитьОбработчикОжидания() с небольшой задержкой. Тогда при быстрой прокрутке стрелками сервер не будет вызываться на каждую строку.
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
ОтключитьОбработчикОжидания("ОбновитьПанельСведений");
ПодключитьОбработчикОжидания("ОбновитьПанельСведений", 0.2, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьПанельСведений()
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ЗаполнитьСведенияНаСервере(ТекущиеДанные.Ссылка);
КонецПроцедуры
Процедуру ЗаполнитьСведенияНаСервере напишите под свою форму: она получает ссылку и заполняет реквизиты панели.