ВыделенныеСтроки в управляемой форме: код, типы ключей и ловушки

· 9 мин

Содержание

Выделенные строки динамического списка в 1С 8.3 получают через свойство элемента формы: Элементы.Список.ВыделенныеСтроки. Это массив ключей строк. Для списка справочника или документа ключ — сама ссылка, её можно сразу передать на сервер и обработать одним запросом.

Типичная боль: обработчик работает с ТекущиеДанные, пользователь выделил 40 документов, а изменился один. Ниже код для управляемой формы, обычной формы и табличного документа, плюс разбор типов, которые реально лежат в массиве.

Табличное поле и динамический список: в чём разница для выделения

Механизм выделения зависит от того, какая форма и какой источник данных у таблицы. Путаница между ними — главный источник неработающего кода.

Где таблицаЧто возвращает ВыделенныеСтрокиКак получить данные строки
Управляемая форма, динамический список с основной таблицейМассив ссылок (СправочникСсылка, ДокументСсылка)Запросом на сервере по ссылкам
Управляемая форма, динамический список регистраМассив ключей записи (РегистрСведенийКлючЗаписи.<Имя>)ДанныеСтроки(Ключ) или запрос
Управляемая форма, табличная часть или таблица значений реквизита формыМассив идентификаторов строк (числа)Коллекция.НайтиПоИдентификатору(Ид)
Обычная форма, ТабличноеПоле со списком справочника/документаКоллекция ВыделенныеСтроки со ссылкамиЧерез ссылку
Обычная форма, ТабличноеПоле с таблицей значенийКоллекция строк таблицы значенийПрямое обращение к колонкам

📌 Ключевое отличие: в управляемой форме динамический список не хранит данные на клиенте целиком. Клиент получает только видимую порцию строк. Поэтому ссылки в массиве есть всегда, а полные данные строк — нет.

В обычной форме ТабличноеПоле работает в толстом клиенте, там данные доступны напрямую, без разделения на клиент и сервер.

Базовый код: как получить выделенные строки и передать их на сервер

Стандартная схема из трёх частей:

  1. Команда формы с обработчиком &НаКлиенте.
  2. Сбор массива ВыделенныеСтроки и проверка, что он не пуст.
  3. Один вызов сервера, куда массив передаётся параметром.
&НаКлиенте
Процедура ОбработатьВыделенные(Команда)

    Выделенные = Элементы.Список.ВыделенныеСтроки;

    Если Выделенные.Количество() = 0 Тогда
        ПоказатьПредупреждение(, "Выделите хотя бы одну строку списка.");
        Возврат;
    КонецЕсли;

    Обработано = ОбработатьНаСервере(Выделенные);

    Элементы.Список.Обновить();
    ПоказатьОповещениеПользователя("Готово",, СтрШаблон("Обработано строк: %1", Формат(Обработано, "ЧГ=0")));

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

&НаСервереБезКонтекста
Функция ОбработатьНаСервере(Ссылки)

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

    Выборка = Запрос.Выполнить().Выбрать();
    Счетчик = 0;
    Пока Выборка.Следующий() Цикл
        // здесь ваша логика: запись в регистр, формирование документа и т.п.
        Счетчик = Счетчик + 1;
    КонецЦикла;

    Возврат Счетчик;

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

💡 &НаСервереБезКонтекста здесь не случайно. Форма не гоняется на сервер, передаётся только массив ссылок. На списке из нескольких тысяч строк разница во времени заметна.

Чтобы пользователь вообще мог выделить несколько строк , у элемента таблицы должно стоять свойство РежимВыделения = Множественный. Его задают в палитре свойств элемента или кодом:

Элементы.Список.РежимВыделения = РежимВыделенияТаблицы.Множественный;

Если команда общая, а не командой формы

Для общей команды или команды объекта с параметром типа ссылки в свойствах команды выставьте Режим использования параметра = Множественный. Тогда в ПараметрКоманды придёт массив выделенных ссылок, и ВыделенныеСтроки читать не нужно:

&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    // ПараметрКоманды — Массив ссылок при множественном режиме
    ОбработатьНаСервере(ПараметрКоманды);
КонецПроцедуры

Что лежит в массиве: ссылки, ключи записей и строки группировок

Перед обработкой проверяйте тип элемента массива. В одном списке могут оказаться значения разных типов.

  • 📁 Иерархический справочник — выделенные группы приходят теми же ссылками, что и элементы. Отсекайте их условием НЕ ЭтоГруппа в запросе или проверкой реквизита.
  • 🗂 Список с группировкой (настроена в пользовательских настройках или в «Группировке» динамического списка) — в массиве могут быть строки группировок типа СтрокаГруппировкиДинамическогоСписка. Ссылкой они не являются.
  • 📒 Список регистра сведений — ключи записи. Отбирать по ним запросом напрямую неудобно, проще взять поля через ДанныеСтроки.
  • 🧩 Динамический список по произвольному запросу без основной таблицы — ключ служебный, ссылкой не является. Нужные поля берите через ДанныеСтроки.

Фильтр от лишних типов на клиенте:

&НаКлиенте
Функция ТолькоСсылки(Выделенные)

    Результат = Новый Массив;
    Для Каждого Ключ Из Выделенные Цикл
        Если ТипЗнч(Ключ) = Тип("СтрокаГруппировкиДинамическогоСписка") Тогда
            Продолжить;
        КонецЕсли;
        Результат.Добавить(Ключ);
    КонецЦикла;
    Возврат Результат;

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

Как прочитать поля строки на клиенте

Метод таблицы формы ДанныеСтроки(Ключ) возвращает данные строки с колонками, которые есть в списке:

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

    Для Каждого Ключ Из Элементы.Список.ВыделенныеСтроки Цикл
        Данные = Элементы.Список.ДанныеСтроки(Ключ);
        Если Данные = Неопределено Тогда
            Продолжить;
        КонецЕсли;
        Сообщить(Данные.Наименование);
    КонецЦикла;

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

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

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

Выделенные области табличного документа

Иногда «строки» — это не список, а отчёт в поле табличного документа: пользователь выделяет диапазон и нажимает кнопку. Здесь работает свойство ВыделенныеОбласти самого табличного документа. Это коллекция областей, у каждой есть границы Верх, Низ, Лево, Право.

Пользователь может выделить несколько несмежных диапазонов с Ctrl, поэтому обходите всю коллекцию:

&НаКлиенте
Процедура СобратьВыделенное(Команда)

    Номера = Новый Массив;

    Для Каждого Обл Из ТабДок.ВыделенныеОбласти Цикл
        Если ТипЗнч(Обл) <> Тип("ОбластьЯчеекТабличногоДокумента") Тогда
            Продолжить; // рисунки и прочие объекты пропускаем
        КонецЕсли;
        Для НомерСтроки = Обл.Верх По Обл.Низ Цикл
            Если Номера.Найти(НомерСтроки) = Неопределено Тогда
                Номера.Добавить(НомерСтроки);
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;

    Для Каждого НомерСтроки Из Номера Цикл
        Ячейка = ТабДок.Область(НомерСтроки, 2, НомерСтроки, 2);
        Сообщить(СтрШаблон("Строка %1: %2", Формат(НомерСтроки, "ЧГ=0"), Ячейка.Текст));
    КонецЦикла;

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

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

💡 Текст ячейки — плохой источник ссылки на объект. При формировании отчёта кладите ссылку в Расшифровка нужной ячейки. Тогда по выделению берёте Ячейка.Расшифровка и получаете настоящую ссылку, а не строку с наименованием.

Если пользователь выделил целые строки кликом по заголовкам, у области тип Строки, и Лево/Право указывают не на реальные колонки. Номера строк при этом корректны. Проверяйте Обл.ТипОбласти, если обращаетесь к колонкам по границам области.

Для отчётов на СКД расшифровка — служебный идентификатор, а не ссылка. Её разбирают через данные расшифровки отчёта, это отдельная задача.

Обычные формы: ВыделенныеСтроки у ТабличногоПоля

В толстом клиенте с обычными формами у ТабличноеПоле тоже есть свойство ВыделенныеСтроки, но это коллекция, а не массив. Обход тот же — Для Каждого:

Процедура КнопкаОбработатьНажатие(Кнопка)

    Для Каждого Стр Из ЭлементыФормы.СправочникСписок.ВыделенныеСтроки Цикл
        // для списка справочника Стр — ссылка
        Если Стр.ЭтоГруппа Тогда
            Продолжить;
        КонецЕсли;
        Сообщить(Стр.Наименование);
    КонецЦикла;

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

Обращение Стр.Наименование в цикле здесь читает данные объекта из базы. На десятке строк это незаметно, на тысяче — лучше собрать ссылки в массив и сделать один запрос.

Если ТабличноеПоле связано с таблицей значений, в коллекции лежат строки таблицы значений. К колонкам обращайтесь напрямую: Стр.Количество, Стр.Цена.

Особенности управляемых форм: клиент, сервер и идентификаторы

В управляемой форме код выполняется в двух контекстах, и это влияет на работу с выделением.

  • 🖥 Элементы.Список.ВыделенныеСтроки читается на клиенте. На сервере элемент формы есть, но выделение там бессмысленно: смотрите его на клиенте и передавайте результат параметром.
  • 🔁 После записи изменений вызывайте Элементы.Список.Обновить(), иначе пользователь увидит старые значения. Выделение при обновлении обычно сохраняется, если ключи строк не изменились.
  • 🧾 Для табличной части в форме документа (Объект.Товары) ВыделенныеСтроки возвращает идентификаторы, а не номера строк и не сами строки.

Пример для табличной части:

&НаКлиенте
Процедура ОбнулитьСкидку(Команда)

    Для Каждого Ид Из Элементы.Товары.ВыделенныеСтроки Цикл
        Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
        Строка.ПроцентСкидки = 0;
    КонецЦикла;

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

Идентификатор не равен НомерСтроки и не равен индексу. Брать Объект.Товары[Ид] — ошибка, которая на первых строках иногда даже «работает», а потом изменяет не ту строку.

Одна выделенная строка против текущей

ТекущаяСтрока и ТекущиеДанные относятся к строке под курсором. При множественном выделении курсор стоит на одной из выделенных строк, но не обязательно на первой. Для команд «над выделенными» используйте только ВыделенныеСтроки, для действий «над строкой под курсором» — ТекущиеДанные.

Сумма по выделенным строкам в подвале формы

Частая просьба бухгалтеров: выделил документы в журнале — увидел их сумму, как в Excel. Платформа сама так не умеет, но это делается в три шага.

  1. Добавьте в форму реквизит СуммаВыделенных (число) и выведите его надписью под списком.
  2. Убедитесь, что колонка СуммаДокумента есть в списке, иначе ДанныеСтроки её не вернёт.
  3. В обработчике ПриАктивизацииСтроки списка подключите обработчик ожидания, чтобы пересчёт не запускался на каждое движение курсора.
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    ОтключитьОбработчикОжидания("ПересчитатьСуммуВыделенных");
    ПодключитьОбработчикОжидания("ПересчитатьСуммуВыделенных", 0.3, Истина);
КонецПроцедуры

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

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

Массовое выделение: как не уронить производительность

Пользователь нажал Ctrl+A в списке из 20 000 документов и запустил обработку. Плохой код превратит это в 20 000 вызовов сервера.

ПодходВызовов сервераКогда годится
ПолучитьОбъект() по каждой ссылке в цикле на сервере1, но тысячи чтений объектовТолько если объект нужно изменить и записать
Вызов сервера внутри клиентского циклаПо числу строкНикогда
Один вызов, один запрос ГДЕ Ссылка В (&Ссылки)1Чтение реквизитов, проверки, отбор
Передача на сервер отбора списка вместо массива1Обработка «всех по условию», когда выделено почти всё

Практические приёмы:

  • ⚡ Читайте реквизиты одним запросом. В конфигурациях на БСП для этого есть ОбщегоНазначения.ЗначенияРеквизитовОбъектов(Ссылки, "Реквизит1, Реквизит2").
  • 📦 Записывайте объекты пачками в транзакции, например по 500. Одна транзакция на 20 000 документов держит блокировки долго и мешает другим пользователям.
  • ⏳ Долгую обработку выносите в фоновое задание, а клиенту показывайте ход выполнения. Иначе форма «зависает», и пользователь кликает по кнопке повторно.
  • 🎯 Если выделено почти всё, спросите себя, не проще ли обработать данные по отбору, который пользователь настроил в списке. Массив из десятков тысяч ссылок дороже передавать, чем условие.

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

Частые ошибки при работе с выделенными строками

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