ТекущиеДанные, ТекущаяСтрока и ВыделенныеСтроки: как прочитать выбор пользователя в таблице формы
Содержание
Если нужны значения полей строки, на клиенте берут Элементы.ИмяТаблицы.ТекущиеДанные. Если нужен идентификатор строки, чтобы найти её на сервере, берут Элементы.ИмяТаблицы.ТекущаяСтрока. Если пользователь отметил несколько строк, читают массив Элементы.ИмяТаблицы.ВыделенныеСтроки. Три свойства, три разных результата, и путаница между ними даёт большинство ошибок новичков.
Код ниже написан для управляемых форм 1С 8.3 (интерфейс «Такси»). Таблица на форме здесь называется Товары, она связана с табличной частью документа Объект.Товары. Своё имя подставьте сами.
Что вернёт каждое свойство таблицы формы
Сначала сводка, потом подробности.
| Свойство | Что возвращает | Где доступно | Когда брать |
|---|---|---|---|
ТекущиеДанные | Данные текущей строки, к полям обращаются через точку | Только клиент | Нужно прочитать или изменить значения |
ТекущаяСтрока | Идентификатор текущей строки (число) | Клиент | Нужно передать строку на сервер |
ВыделенныеСтроки | Массив идентификаторов всех выделенных строк | Клиент | Пользователь работает с группой строк |
ТекущийЭлемент | Колонка, в которой стоит курсор | Клиент | Нужно знать, какое поле редактируется |
Идентификатор строки не равен номеру строки в таблице. Это внутренний ключ формы. Он не меняется при сортировке и отборе, а вот порядковый номер меняется.
Свойства относятся к элементу формы, а не к данным. Поэтому обращаться к ним нужно через коллекцию Элементы, а не через Объект.
Получение текущей строки в клиентской процедуре
Самый частый случай: пользователь поставил курсор на строку и нажал кнопку командной панели. В обработчике команды читаем ТекущиеДанные.
&НаКлиенте
Процедура ПоказатьСтроку(Команда)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
Сообщить(ТекущиеДанные.Номенклатура + ": " + Формат(ТекущиеДанные.Сумма, "ЧГ=0"));
КонецПроцедуры
Проверка на Неопределено обязательна. У пустой таблицы текущей строки нет, и обращение к полю у неопределённого значения роняет процедуру с ошибкой «Значение не является значением объектного типа
».
Что важно знать про ТекущиеДанные:
- ✅ через них можно не только читать, но и записывать:
ТекущиеДанные.Количество = 5сразу меняет значение в таблице; - ✅ поля называются так же, как реквизиты табличной части;
- ⚠️ это не копия строки, а ссылка на данные формы, поэтому изменения видны сразу;
- ⚠️ на сервере свойство недоступно, оно существует только в клиентском контексте.
Как узнать, в какой колонке стоит курсор
Свойство Элементы.Товары.ТекущийЭлемент возвращает элемент формы, то есть колонку. Сравните его имя с нужным:
Если Элементы.Товары.ТекущийЭлемент.Имя = "ТоварыКоличество" Тогда
// курсор стоит в колонке «Количество»
КонецЕсли;
Имя колонки состоит из имени таблицы и имени реквизита, в данном примере ТоварыКоличество. Точное имя смотрите в конфигураторе, в дереве элементов формы.
Строка из модуля объекта: как передать выбор на сервер
В модуле объекта документа и в серверных процедурах формы элементов формы нет. Вызвать оттуда Элементы.Товары.ТекущиеДанные нельзя. Выбор пользователя нужно передать параметром.
Для одной строки передаём идентификатор:
&НаКлиенте
Процедура ОбработатьСтроку(Команда)
Ид = Элементы.Товары.ТекущаяСтрока;
Если Ид = Неопределено Тогда
Возврат;
КонецЕсли;
ОбработатьСтрокуНаСервере(Ид);
КонецПроцедуры
&НаСервере
Процедура ОбработатьСтрокуНаСервере(Ид)
Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
Если Строка <> Неопределено Тогда
Строка.Сумма = Строка.Цена * Строка.Количество;
КонецЕсли;
КонецПроцедуры
Метод НайтиПоИдентификатору есть у коллекции данных формы. Он возвращает строку, с которой уже можно работать как с обычной строкой табличной части.
Вторая возможность: передать на сервер не идентификатор, а нужные значения, то есть номенклатуру, количество, цену. Так проще, когда серверной процедуре не нужна вся строка целиком и она вызывается из разных мест.
Если логика живёт в модуле самого документа (например, в ОбработкаПроведения), выделения там нет вообще. Модуль объекта работает с данными, а не с интерфейсом. Передавайте нужные строки через параметры процедур или храните признак в реквизите табличной части, например флажок Отметка.
Работа с множественным выделением строк
Когда пользователь выделил строки с зажатым Ctrl или Shift, ТекущиеДанные покажет только одну из них, ту, где стоит курсор. Остальные нужно брать из ВыделенныеСтроки.
&НаКлиенте
Процедура СуммаВыделенных(Команда)
Итого = 0;
Для Каждого Ид Из Элементы.Товары.ВыделенныеСтроки Цикл
Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
Итого = Итого + Строка.Сумма;
КонецЦикла;
Сообщить("Сумма выделенных строк: " + Формат(Итого, "ЧДЦ=2"));
КонецПроцедуры
На клиенте Объект.Товары доступен для чтения, поэтому НайтиПоИдентификатору здесь работает. Но если строк тысячи, вызывать сервер одной пачкой быстрее. Отправьте массив идентификаторов целиком:
&НаКлиенте
Процедура ОтметитьВыделенные(Команда)
ОтметитьВыделенныеНаСервере(Элементы.Товары.ВыделенныеСтроки);
КонецПроцедуры
&НаСервере
Процедура ОтметитьВыделенныеНаСервере(Идентификаторы)
Для Каждого Ид Из Идентификаторы Цикл
Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
Строка.Отметка = Истина;
КонецЦикла;
КонецПроцедуры
Удаление выделенных строк
Здесь ломается больше всего кода. Нельзя удалять строки и одновременно идти по ВыделенныеСтроки, потому что массив зависит от выделения, а после удаления оно меняется. Сначала соберите объекты строк, потом удаляйте:
&НаСервере
Процедура УдалитьВыделенныеНаСервере(Идентификаторы)
Удаляемые = Новый Массив;
Для Каждого Ид Из Идентификаторы Цикл
Удаляемые.Добавить(Объект.Товары.НайтиПоИдентификатору(Ид));
КонецЦикла;
Для Каждого Строка Из Удаляемые Цикл
Объект.Товары.Удалить(Строка);
КонецЦикла;
КонецПроцедуры
Если таблица пользователя отсортирована или отфильтрована, не рассчитывайте на порядок в массиве ВыделенныеСтроки. Идентификаторы не обязаны идти по возрастанию.
Нужно ограничить выбор одной строкой? У таблицы формы есть свойство режима выделения, его выставляют в свойствах элемента в конфигураторе. Если стоит одиночный режим, ВыделенныеСтроки всегда содержит не больше одного элемента.
События таблицы, на которых строку читают чаще всего
Выбор часто нужен не по кнопке, а автоматически, когда пользователь перешёл на другую строку или правит ячейку. Для этого в свойствах таблицы назначают обработчики.
| Событие | Когда срабатывает | Что учесть |
|---|---|---|
ПриАктивизацииСтроки | Курсор перешёл на другую строку | ТекущиеДанные может быть Неопределено |
ПриАктивизацииЯчейки | Курсор перешёл в другую ячейку | Колонку определяют по ТекущийЭлемент |
ВыборСтроки | Двойной щелчок или Enter на строке | Идентификатор строки приходит параметром |
ПриНачалеРедактирования | Строка перешла в режим правки | Параметр НоваяСтрока отличает добавление от правки |
ПриОкончанииРедактирования | Правка завершена | Удобно пересчитывать итоги строки |
ПередУдалением | Перед удалением строки | Можно отказаться, установив Отказ = Истина |
В событии ВыборСтроки параметры формы дают идентификатор выбранной строки, а не её данные, поэтому там снова пригодится НайтиПоИдентификатору.
С ПриАктивизацииСтроки связана ловушка. Событие срабатывает очень часто: при открытии формы, при каждой прокрутке стрелками, после обновления данных. Тяжёлые обращения к серверу вешать на него напрямую нельзя, форма начнёт тормозить. Типичное решение выглядит так:
- В обработчике событие только запоминает идентификатор текущей строки.
- Запускается подключаемый обработчик ожидания
ПодключитьОбработчикОжидания("ОбновитьПанель", 0.2, Истина). - Если пользователь быстро листает строки, предыдущий запуск отменяется, а нужный срабатывает только когда курсор остановился.
Отмену делают командой ОтключитьОбработчикОжидания("ОбновитьПанель") перед новым подключением.
Типичные ошибки и их причины
Ошибка: «Поле объекта не обнаружено» при обращении к ТекущиеДанные. Колонка называется иначе, чем вы написали, или реквизит не включён в форму. Сверьте имя в дереве реквизитов формы.
Ошибка: «Значение не является значением объектного типа». Таблица пуста, ТекущиеДанные равно Неопределено. Добавьте проверку, как в первом примере.
Вместо данных строки приходит число. Вы взяли ТекущаяСтрока и пытаетесь читать поле. Число нужно превратить в строку через НайтиПоИдентификатору.
Строка из ВыделенныеСтроки не находится. Бывает, когда после изменения таблицы вы используете старые идентификаторы. Читайте массив заново прямо перед обработкой.
Строки удаляются не все. Вы удаляете в цикле по ВыделенныеСтроки. Используйте схему «собрать, потом удалить» из раздела выше.
Серверная процедура падает на Элементы. Контекст не тот. Прочитайте значение на клиенте и передайте в параметре.
Не меняйте табличную часть по индексам, взятым из интерфейса. Индекс строки в списке, отсортированном пользователем, не совпадает с её местом в Объект.Товары.
Дополнительные приёмы: текущая строка из таблицы значений и запрос
Не всякая таблица на форме связана с табличной частью. Если это реквизит формы типа ТаблицаЗначений, всё работает так же: Элементы.ТаблицаЗначений.ТекущиеДанные и НайтиПоИдентификатору у ТаблицаЗначений на форме доступны одинаково, потому что на форме это коллекция данных формы.
Если нужен номер строки в таблице, а не идентификатор, воспользуйтесь методом Индекс:
Ид = Элементы.Товары.ТекущаяСтрока;
Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
Номер = Объект.Товары.Индекс(Строка) + 1;
Индекс начинается с нуля, поэтому для порядкового номера «как видит пользователь» добавляют единицу.
Чтобы поставить курсор на нужную строку программно, присвойте идентификатор свойству ТекущаяСтрока:
Элементы.Товары.ТекущаяСтрока = Строка.ПолучитьИдентификатор();
Это пригодится после добавления строки кодом: пользователь сразу видит новую позицию и может править её.
Ещё один приём: при добавлении строки программно получить её идентификатор сразу из результата Объект.Товары.Добавить(). Затем передайте его в ТекущаяСтрока, и фокус окажется на новой позиции.
Если нужно только прочитать значение одной колонки у всех выделенных строк без обращения к серверу, достаточно клиентского цикла из раздела про множественное выделение. Серверный вызов оправдан, когда нужно записывать в базу, запрашивать данные или менять много строк за раз.
Пересчёт строки при правке: готовый пример
Типовая задача: пользователь меняет количество или цену, а сумма в этой же строке должна обновиться. Обработчик ПриИзменении колонки работает в контексте текущей строки, и ТекущиеДанные здесь именно то, что нужно:
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ТекущиеДанные.Сумма = ТекущиеДанные.Количество * ТекущиеДанные.Цена;
КонецПроцедуры
Обработчик пишется для каждой колонки, от которой зависит сумма (Количество, Цена). Чтобы не дублировать код, вынесите расчёт в отдельную клиентскую процедуру ПересчитатьСтроку(Строка) и вызывайте её из обоих обработчиков.
Проверьте пример на числах. Количество 4, цена 250: сумма 1000. Меняем количество на 6, сумма становится 1500. Если после правки в таблице осталось 1000, обработчик не привязан к колонке: откройте свойства колонки на форме и проверьте событие ПриИзменении.
Когда одной клиентской логики мало
Если расчёт тянет данные из базы (цену из регистра, ставку НДС из справочника), клиентский обработчик вызывает серверную процедуру и передаёт ей значения, а не всю строку:
- На клиенте читаем номенклатуру из
ТекущиеДанные. - Вызываем серверную функцию, она возвращает цену.
- Присваиваем цену обратно в
ТекущиеДанные.Ценаи пересчитываем сумму.
Так сервер не знает об элементах формы, а клиент не ходит в базу напрямую. Серверный вызов делается один раз на изменение, а не на каждый кадр прокрутки.
Какой способ выбрать
| Задача | Что использовать |
|---|---|
| Прочитать поля текущей строки на клиенте | ТекущиеДанные |
| Найти строку на сервере | ТекущаяСтрока + НайтиПоИдентификатору |
| Обработать группу строк | ВыделенныеСтроки |
| Узнать колонку | ТекущийЭлемент |
| Реагировать на смену строки | ПриАктивизацииСтроки + обработчик ожидания |
| Поставить курсор на строку | Присвоить идентификатор в ТекущаяСтрока |
Частые вопросы
Можно ли использовать `ТекущиеДанные` в серверной процедуре?
Чем `ТекущаяСтрока` отличается от `ТекущиеДанные`?
ТекущаяСтрока возвращает идентификатор, число, по которому строку находят в коллекции. ТекущиеДанные возвращает саму строку с полями.Как узнать, что пользователь выделил несколько строк?
Элементы.Товары.ВыделенныеСтроки. Если оно больше единицы, выделение множественное.Почему `ПриАктивизацииСтроки` срабатывает при открытии формы?
Как получить номер выделенной строки в списке?
НайтиПоИдентификатору и вызовите Индекс у коллекции, добавив единицу к результату.