Как в 1С прочитать выделенные строки таблицы на форме

· 9 мин

Содержание

Выделенные строки табличной части на управляемой форме отдаёт свойство Элементы.ИмяТаблицы.ВыделенныеСтроки. Это массив идентификаторов строк, а не самих строк. Чтобы получить строку, берёте идентификатор и вызываете НайтиПоИдентификатору() у данных формы.

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

Чем выделенные строки отличаются от текущей

В управляемой форме у табличной части есть два разных понятия. Их часто путают, и код работает «через раз».

Что нужноЧем получитьЧто вернётся
Одна строка под курсоромЭлементы.Товары.ТекущиеДанныеСтрока данных формы или Неопределено
Идентификатор строки под курсоромЭлементы.Товары.ТекущаяСтрокаЧисло (или ключ для динамического списка)
Все отмеченные строкиЭлементы.Товары.ВыделенныеСтрокиМассив идентификаторов

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

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

Где работает свойство: клиент и сервер

Элементы — это элементы формы, они существуют только на клиенте. Серверная процедура с директивой &НаСервере до них не дотянется напрямую.

Схема всегда одна:

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

Не передавайте на сервер сами идентификаторы строк, если дальше работаете с объектом, прочитанным заново. Идентификатор относится к данным формы, а не к строке в базе. Надёжнее передавать номер строки или значение ключевой колонки.

Обход выделенных строк: базовый вариант

Начнём с минимального примера. Табличная часть Товары документа, нужна сумма по выделенным строкам:

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

	Выделенные = Элементы.Товары.ВыделенныеСтроки;

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

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

	ПоказатьПредупреждение(, "Сумма по выделенным: " + Формат(Итого, "ЧДЦ=2"));

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

Что здесь важно:

  • 📌 Объект.Товары — данные формы, поэтому поиск по идентификатору идёт именно в них.
  • 📌 Результат НайтиПоИдентификатору() — строка, у которой можно читать и менять колонки.
  • 📌 Число перед склейкой со строкой приведено через Формат(), иначе получите ошибку преобразования.

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

Как собрать из выделенного таблицу значений

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

Соберём её на клиенте из выделенных строк:

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

	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Номенклатура");
	Таблица.Колонки.Добавить("Количество");
	Таблица.Колонки.Добавить("Сумма");

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

	Возврат Таблица;

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

ЗаполнитьЗначенияСвойств копирует только колонки с совпадающими именами, остальные игнорирует. Лишние колонки в строке табличной части ему не мешают.

Таблицу значений можно передать в серверный метод параметром: платформа сериализует её при вызове. Для небольших выборок (десятки или сотни строк) это нормально.

Метод Выгрузить для серверных коллекций

На сервере у табличной части объекта (не данных формы) есть метод Выгрузить(). Он возвращает таблицу значений: целиком или только по заданным строкам и колонкам.

Вариант для случая, когда на клиенте вы собрали номера выделенных строк:

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

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

	Если Номера.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;

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

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

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

	ДокОбъект = РеквизитФормыВЗначение("Объект");

	Для Каждого Номер Из Номера Цикл
		СтрокаТЧ = ДокОбъект.Товары[Номер - 1];
		// здесь работа с СтрокаТЧ
	КонецЦикла;

	ЗначениеВРеквизитФормы(ДокОбъект, "Объект");

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

НомерСтроки у табличной части начинается с 1, а индекс [...] — с 0. Отсюда Номер - 1. Это самый частый источник «съехавших» строк.

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

Сколько строк выделено и как обработать удаление

Когда внутри цикла вы удаляете строки, индексы двигаются. Классическая ловушка: удаляете строку, а следующая из массива уже указывает не туда.

Безопасно удалять так: сначала собрать идентификаторы, потом пройти и удалить каждую по найденной строке. Идентификатор строки остаётся стабильным после удаления соседней:

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

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

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

Проверка на Неопределено защищает от случая, когда строку уже убрали. Копия массива в переменной Ид нужна, чтобы не зависеть от того, как платформа обновляет свойство в процессе.

Если вы идёте по индексам, обходите в обратном порядке: Для Сч = 1 По Коллекция.Количество() Цикл, элемент Коллекция[Коллекция.Количество() - Сч]. Тогда удаление не сдвигает ещё не пройденные элементы.

Выделенные строки динамического списка

В списках документов и справочников (динамический список) логика та же, но идентификатор — это не число, а ключ строки. Для списка документов ключом оказывается ссылка на документ.

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

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

	Если Ссылки.Количество() > 0 Тогда
		ПровестиДокументыНаСервере(Ссылки);
	КонецЕсли;

КонецПроцедуры
&НаСервере
Процедура ПровестиДокументыНаСервере(Ссылки)

	Для Каждого Ссылка Из Ссылки Цикл
		ДокОбъект = Ссылка.ПолучитьОбъект();
		ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
	КонецЦикла;

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

Что нужно знать про списки:

  • 📌 Для справочников и документов ключ — ссылка, её сразу можно передавать на сервер.
  • 📌 Для регистров сведений ключ — особый объект «ключ записи», а не ссылка. Читать из него поля напрямую нельзя, сначала нужно получить данные строки запросом или через Элементы.Список.ТекущиеДанные.
  • 📌 В иерархическом списке в выделение могут попасть группы. Если команда работает только с элементами, проверяйте это на сервере: у ссылки справочника есть реквизит ЭтоГруппа.
  • 📌 При большом числе выделенных документов проведение в цикле на сервере занимает время. Для сотен документов лучше отдельное фоновое задание.

Типичные ошибки при чтении выделения

Ошибки в этой теме повторяются, и большинство лечится одной проверкой.

«Поле объекта не обнаружено» при обращении к Элементы... на сервере. Вы вызываете свойство из процедуры &НаСервере. Прочитайте выделение на клиенте и передайте результат параметром.

НайтиПоИдентификатору возвращает Неопределено. Идентификатор взят от другой таблицы или строка уже удалена. Проверяйте результат, как в примере выше, и ищите в том же наборе данных, из которого брали выделение.

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

Таблица не меняется после обработки. На сервере вы работали с копией объекта, полученной через РеквизитФормыВЗначение, и не вернули её обратно. Нужен вызов ЗначениеВРеквизитФормы.

Ошибка при склейке числа и строки. Результат арифметики нельзя прибавлять к тексту без преобразования. Используйте Формат(Число, "ЧГ=0") или СтрШаблон("Строк: %1", Число).

Полный пример: кнопка «Скопировать выделенное в буфер»

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

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

	Выделенные = Элементы.Товары.ВыделенныеСтроки;

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

	Строки = Новый Массив;

	Для Каждого ИдСтроки Из Выделенные Цикл
		СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(ИдСтроки);
		Если СтрокаТЧ = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Строки.Добавить(СтрШаблон("%1 — %2", СтрокаТЧ.Номенклатура, СтрокаТЧ.Количество));
	КонецЦикла;

	Текст = СтрСоединить(Строки, Символы.ПС);
	ПоказатьПредупреждение(, Текст);

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

Порядок рассуждений тот же для любой команды над выделением:

  1. Прочитать ВыделенныеСтроки.
  2. Проверить, что массив не пуст.
  3. Найти строку по идентификатору и проверить на Неопределено.
  4. Взять нужные колонки.
  5. Если требуется сервер, передать массив значений параметром.

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

Как не потерять выделение и ускорить работу

Несколько приёмов, которые пригодятся на реальных формах.

  • ⚡ Читайте ВыделенныеСтроки один раз в начале процедуры и работайте с копией. Повторные обращения к свойству в цикле ничего не дают.
  • ⚡ Передавайте на сервер минимум данных: номера строк или ссылки, а не целую таблицу, если сервер всё равно перечитает документ.
  • ⚡ Не вызывайте серверную процедуру в цикле по каждой выделенной строке. Один вызов с массивом быстрее многих вызовов с одним элементом, потому что каждый вызов — обращение к серверу.
  • ⚡ Если нужен итог по выделению (сумма, количество) без записи данных, считайте его на клиенте: это не требует вызова сервера.

Хотите показывать сумму выделенного в подвале без нажатия кнопки? Подключите обработчик события выделения таблицы и пересчитывайте итог там. Но не запускайте тяжёлые серверные вызовы на каждое движение курсора: форма начнёт тормозить.

Режим выделения: почему выделяется только одна строка

Если пользователь жалуется, что мышью и клавишей Shift выделить несколько строк нельзя, дело не в коде. У таблицы на форме есть свойство режима выделения.

Откройте форму в конфигураторе, выберите элемент таблицы и посмотрите палитру свойств. Для множественного выделения нужно значение «Множественный». Программно то же задают свойством РежимВыделения:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	Элементы.Товары.РежимВыделения = РежимВыделенияТаблицы.Множественный;
КонецПроцедуры

В режиме «Одиночный» массив ВыделенныеСтроки всегда содержит не больше одного элемента. Код при этом не ломается, он просто обрабатывает одну строку.

Какой способ выбрать

ЗадачаЧто использоватьРиск
Прочитать одну строкуТекущиеДанныеВернёт Неопределено на пустой таблице
Обработать несколько строк на клиентеВыделенныеСтроки + НайтиПоИдентификаторуИдентификатор действует только в этой форме
Отдать выделение на серверМассив номеров строк или ссылокНомера устаревают, если таблицу меняли
Передать выделение в печать или отчётТаблица значений из выделенных строкОбъём при тысячах строк
Массовая операция над документами из спискаМассив ссылок из ВыделенныеСтроки спискаВремя проведения при сотнях документов

Выделение и права пользователя

Команда над выделением выполняется от имени текущего пользователя, поэтому серверная процедура упирается в его права. Если кнопка «Провести выделенные» у кого-то падает с ошибкой прав, проверьте роль пользователя, а не логику обхода.

Полезная практика для массовых команд:

  • ✅ Собирайте результат по каждой строке: что обработано, что нет, по какой причине.
  • ✅ Показывайте итог одним сообщением в конце, а не десятком предупреждений подряд.
  • ✅ Ловите ошибку записи внутри цикла через Попытка ... Исключение, чтобы один плохой документ не останавливал остальные.
  • ✅ Для длинных операций выводите состояние через Состояние("Обработано ...").

Заготовка на сервере с учётом этих пунктов:

&НаСервере
Функция ПровестиСОтчётом(Ссылки)

	Ошибки = Новый Массив;

	Для Каждого Ссылка Из Ссылки Цикл
		Попытка
			ДокОбъект = Ссылка.ПолучитьОбъект();
			ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
		Исключение
			Ошибки.Добавить(Строка(Ссылка) + ": " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		КонецПопытки;
	КонецЦикла;

	Возврат Ошибки;

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

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

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

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