ТекущиеДанные, ТекущаяСтрока и ВыделенныеСтроки: как прочитать выбор пользователя в таблице формы

· 9 мин

Содержание

Если нужны значения полей строки, на клиенте берут Элементы.ИмяТаблицы.ТекущиеДанные. Если нужен идентификатор строки, чтобы найти её на сервере, берут Элементы.ИмяТаблицы.ТекущаяСтрока. Если пользователь отметил несколько строк, читают массив Элементы.ИмяТаблицы.ВыделенныеСтроки. Три свойства, три разных результата, и путаница между ними даёт большинство ошибок новичков.

Код ниже написан для управляемых форм 1С 8.3 (интерфейс «Такси»). Таблица на форме здесь называется Товары, она связана с табличной частью документа Объект.Товары. Своё имя подставьте сами.

Что вернёт каждое свойство таблицы формы

Сначала сводка, потом подробности.

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

Идентификатор строки не равен номеру строки в таблице. Это внутренний ключ формы. Он не меняется при сортировке и отборе, а вот порядковый номер меняется.

Свойства относятся к элементу формы, а не к данным. Поэтому обращаться к ним нужно через коллекцию Элементы, а не через Объект.

Получение текущей строки в клиентской процедуре

Самый частый случай: пользователь поставил курсор на строку и нажал кнопку командной панели. В обработчике команды читаем ТекущиеДанные.

&НаКлиенте
Процедура ПоказатьСтроку(Команда)
	ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;
	Сообщить(ТекущиеДанные.Номенклатура + ": " + Формат(ТекущиеДанные.Сумма, "ЧГ=0"));
КонецПроцедуры

Проверка на Неопределено обязательна. У пустой таблицы текущей строки нет, и обращение к полю у неопределённого значения роняет процедуру с ошибкой «Значение не является значением объектного типа ».

Что важно знать про ТекущиеДанные:

  • ✅ через них можно не только читать, но и записывать: ТекущиеДанные.Количество = 5 сразу меняет значение в таблице;
  • ✅ поля называются так же, как реквизиты табличной части;
  • ⚠️ это не копия строки, а ссылка на данные формы, поэтому изменения видны сразу;
  • ⚠️ на сервере свойство недоступно, оно существует только в клиентском контексте.

Как узнать, в какой колонке стоит курсор

Свойство Элементы.Товары.ТекущийЭлемент возвращает элемент формы, то есть колонку. Сравните его имя с нужным:

Если Элементы.Товары.ТекущийЭлемент.Имя = "ТоварыКоличество" Тогда
	// курсор стоит в колонке «Количество»
КонецЕсли;

Имя колонки состоит из имени таблицы и имени реквизита, в данном примере ТоварыКоличество. Точное имя смотрите в конфигураторе, в дереве элементов формы.

Строка из модуля объекта: как передать выбор на сервер

В модуле объекта документа и в серверных процедурах формы элементов формы нет. Вызвать оттуда Элементы.Товары.ТекущиеДанные нельзя. Выбор пользователя нужно передать параметром.

Для одной строки передаём идентификатор:

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

&НаСервере
Процедура ОбработатьСтрокуНаСервере(Ид)
	Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
	Если Строка <> Неопределено Тогда
		Строка.Сумма = Строка.Цена * Строка.Количество;
	КонецЕсли;
КонецПроцедуры

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

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

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

Работа с множественным выделением строк

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

&НаКлиенте
Процедура СуммаВыделенных(Команда)
	Итого = 0;
	Для Каждого Ид Из Элементы.Товары.ВыделенныеСтроки Цикл
		Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
		Итого = Итого + Строка.Сумма;
	КонецЦикла;
	Сообщить("Сумма выделенных строк: " + Формат(Итого, "ЧДЦ=2"));
КонецПроцедуры

На клиенте Объект.Товары доступен для чтения, поэтому НайтиПоИдентификатору здесь работает. Но если строк тысячи, вызывать сервер одной пачкой быстрее. Отправьте массив идентификаторов целиком:

&НаКлиенте
Процедура ОтметитьВыделенные(Команда)
	ОтметитьВыделенныеНаСервере(Элементы.Товары.ВыделенныеСтроки);
КонецПроцедуры

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

Удаление выделенных строк

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

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

Если таблица пользователя отсортирована или отфильтрована, не рассчитывайте на порядок в массиве ВыделенныеСтроки. Идентификаторы не обязаны идти по возрастанию.

Нужно ограничить выбор одной строкой? У таблицы формы есть свойство режима выделения, его выставляют в свойствах элемента в конфигураторе. Если стоит одиночный режим, ВыделенныеСтроки всегда содержит не больше одного элемента.

События таблицы, на которых строку читают чаще всего

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

СобытиеКогда срабатываетЧто учесть
ПриАктивизацииСтрокиКурсор перешёл на другую строкуТекущиеДанные может быть Неопределено
ПриАктивизацииЯчейкиКурсор перешёл в другую ячейкуКолонку определяют по ТекущийЭлемент
ВыборСтрокиДвойной щелчок или Enter на строкеИдентификатор строки приходит параметром
ПриНачалеРедактированияСтрока перешла в режим правкиПараметр НоваяСтрока отличает добавление от правки
ПриОкончанииРедактированияПравка завершенаУдобно пересчитывать итоги строки
ПередУдалениемПеред удалением строкиМожно отказаться, установив Отказ = Истина

В событии ВыборСтроки параметры формы дают идентификатор выбранной строки, а не её данные, поэтому там снова пригодится НайтиПоИдентификатору.

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

  1. В обработчике событие только запоминает идентификатор текущей строки.
  2. Запускается подключаемый обработчик ожидания ПодключитьОбработчикОжидания("ОбновитьПанель", 0.2, Истина).
  3. Если пользователь быстро листает строки, предыдущий запуск отменяется, а нужный срабатывает только когда курсор остановился.

Отмену делают командой ОтключитьОбработчикОжидания("ОбновитьПанель") перед новым подключением.

Типичные ошибки и их причины

Ошибка: «Поле объекта не обнаружено» при обращении к ТекущиеДанные. Колонка называется иначе, чем вы написали, или реквизит не включён в форму. Сверьте имя в дереве реквизитов формы.

Ошибка: «Значение не является значением объектного типа». Таблица пуста, ТекущиеДанные равно Неопределено. Добавьте проверку, как в первом примере.

Вместо данных строки приходит число. Вы взяли ТекущаяСтрока и пытаетесь читать поле. Число нужно превратить в строку через НайтиПоИдентификатору.

Строка из ВыделенныеСтроки не находится. Бывает, когда после изменения таблицы вы используете старые идентификаторы. Читайте массив заново прямо перед обработкой.

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

Серверная процедура падает на Элементы. Контекст не тот. Прочитайте значение на клиенте и передайте в параметре.

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

Дополнительные приёмы: текущая строка из таблицы значений и запрос

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

Если нужен номер строки в таблице, а не идентификатор, воспользуйтесь методом Индекс:

Ид = Элементы.Товары.ТекущаяСтрока;
Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
Номер = Объект.Товары.Индекс(Строка) + 1;

Индекс начинается с нуля, поэтому для порядкового номера «как видит пользователь» добавляют единицу.

Чтобы поставить курсор на нужную строку программно, присвойте идентификатор свойству ТекущаяСтрока:

Элементы.Товары.ТекущаяСтрока = Строка.ПолучитьИдентификатор();

Это пригодится после добавления строки кодом: пользователь сразу видит новую позицию и может править её.

Ещё один приём: при добавлении строки программно получить её идентификатор сразу из результата Объект.Товары.Добавить(). Затем передайте его в ТекущаяСтрока, и фокус окажется на новой позиции.

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

Пересчёт строки при правке: готовый пример

Типовая задача: пользователь меняет количество или цену, а сумма в этой же строке должна обновиться. Обработчик ПриИзменении колонки работает в контексте текущей строки, и ТекущиеДанные здесь именно то, что нужно:

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

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

Проверьте пример на числах. Количество 4, цена 250: сумма 1000. Меняем количество на 6, сумма становится 1500. Если после правки в таблице осталось 1000, обработчик не привязан к колонке: откройте свойства колонки на форме и проверьте событие ПриИзменении.

Когда одной клиентской логики мало

Если расчёт тянет данные из базы (цену из регистра, ставку НДС из справочника), клиентский обработчик вызывает серверную процедуру и передаёт ей значения, а не всю строку:

  1. На клиенте читаем номенклатуру из ТекущиеДанные.
  2. Вызываем серверную функцию, она возвращает цену.
  3. Присваиваем цену обратно в ТекущиеДанные.Цена и пересчитываем сумму.

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

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

ЗадачаЧто использовать
Прочитать поля текущей строки на клиентеТекущиеДанные
Найти строку на сервереТекущаяСтрока + НайтиПоИдентификатору
Обработать группу строкВыделенныеСтроки
Узнать колонкуТекущийЭлемент
Реагировать на смену строкиПриАктивизацииСтроки + обработчик ожидания
Поставить курсор на строкуПрисвоить идентификатор в ТекущаяСтрока
Проверьте код, прежде чем отдавать его пользователям
Выполнено: 0 / 5

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

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