Выделение строк из кода: списки, табличные части, отчёты и деревья
Содержание
Чтобы программно выделить строку в 1С 8.3, на управляемой форме меняют два свойства элемента ТаблицаФормы: ТекущаяСтрока ставит курсор на строку, а коллекция ВыделенныеСтроки (методы Добавить() и Очистить()) задаёт набор выделенных строк. В динамическом списке в них кладут ссылку на объект, в табличной части и дереве — идентификатор строки, полученный через ПолучитьИдентификатор().
Типичная задача из практики: после проведения обработки пользователь возвращается в список документов, и курсор должен стоять на только что созданном документе, а все документы с ошибками — подсвечиваться. Выделение строк и подсветка цветом в 1С решаются разными механизмами, и путать их — главный источник багов. Ниже разобраны оба, плюс табличный документ и дерево значений. Всё сказанное относится к платформе в целом и не зависит от конфигурации.
Чем текущая строка отличается от выделенных
У таблицы на форме три разные сущности, которые в разговоре называют «выделением»:
- 🎯 Текущая строка (
ТекущаяСтрока) — та, на которой стоит курсор. Она всегда одна. - 📌 Выделенные строки (
ВыделенныеСтроки) — набор строк, отмеченных пользователем черезCtrlилиShift. Текущая строка обычно входит в этот набор. - 🎨 Подсвеченные строки — визуальная отметка цветом через условное оформление. С выделением курсором она никак не связана и сохраняется при перемещении по списку.
Множественное выделение работает, только если у таблицы стоит режим РежимВыделенияТаблицы.Множественный. В конфигураторе это свойство «Режим выделения» элемента формы. Из кода:
Элементы.Список.РежимВыделения = РежимВыделенияТаблицы.Множественный;
Элементы.Список.РежимВыделенияСтроки = РежимВыделенияСтрокиТаблицы.Строка;
Второе свойство определяет, выделяется строка целиком или отдельная ячейка. Для списков документов удобнее режим Строка.
Что кладётся в свойства в зависимости от источника данных:
| Источник данных таблицы | Что в ТекущаяСтрока и ВыделенныеСтроки | Как получить данные строки |
|---|---|---|
| Динамический список объектов | Ссылка (ДокументСсылка, СправочникСсылка) | Сама ссылка или ТекущиеДанные |
| Динамический список регистра | Ключ записи регистра | ТекущиеДанные |
| Табличная часть, таблица значений на форме | Идентификатор строки (число) | Объект.Товары.НайтиПоИдентификатору(Ид) |
| Дерево значений на форме | Идентификатор элемента дерева | Дерево.НайтиПоИдентификатору(Ид) |
На обычных формах (толстый клиент, конфигурации на 8.2) ТабличноеПоле.ВыделенныеСтроки хранит сами строки, а не идентификаторы. Код со старых форумов поэтому часто не работает на управляемых формах.
Как выделить строки в динамическом списке
Динамический список читает данные порциями, поэтому выделять в нём можно только по ключу — ссылке на объект. Платформа сама найдёт строку и прокрутит список к ней.
Поставить курсор на нужный документ
&НаКлиенте
Процедура ПерейтиКДокументу(ДокументСсылка)
Элементы.Список.ТекущаяСтрока = ДокументСсылка;
КонецПроцедуры
Если документ отсечён отбором списка, курсор не переместится и ошибки не будет. Это частый повод для вопросов «почему не работает ».
При открытии формы списка курсор удобно задать в ПриСозданииНаСервере. Формы выбора справочников уже умеют это делать через параметр ТекущаяСтрока, передаваемый в ОткрытьФорму():
ПараметрыФормы = Новый Структура("ТекущаяСтрока", Контрагент);
ОткрытьФорму("Справочник.Контрагенты.ФормаВыбора", ПараметрыФормы, Элементы.Контрагент);
Выделить сразу несколько строк
&НаКлиенте
Процедура ВыделитьДокументы(МассивСсылок)
Элементы.Список.ВыделенныеСтроки.Очистить();
Для Каждого Ссылка Из МассивСсылок Цикл
Элементы.Список.ВыделенныеСтроки.Добавить(Ссылка);
КонецЦикла;
Если МассивСсылок.Количество() > 0 Тогда
Элементы.Список.ТекущаяСтрока = МассивСсылок[0];
КонецЕсли;
КонецПроцедуры
Массив ссылок обычно получают серверной функцией с запросом, например все неоплаченные счета за период. Курсор ставим на первый элемент, чтобы пользователь сразу увидел начало выборки.
Прочитать то, что выделил пользователь
&НаКлиенте
Процедура ОбработатьВыделенные(Команда)
Выделенные = Новый Массив;
Для Каждого Строка Из Элементы.Список.ВыделенныеСтроки Цикл
Если ТипЗнч(Строка) = Тип("СтрокаГруппировкиДинамическогоСписка") Тогда
Продолжить;
КонецЕсли;
Выделенные.Добавить(Строка);
КонецЦикла;
ОбработатьНаСервере(Выделенные);
КонецПроцедуры
Проверка типа нужна, если в списке включена группировка: в выделение попадают строки групп, и серверная процедура упадёт на попытке обработать их как ссылки.
Команду «Выделить все» в динамическом списке нельзя честно повторить кодом: в форме загружена только видимая порция строк. Если нужно обработать все документы по отбору, получайте их запросом на сервере, а не через выделение.
Выделение ячеек и строк в табличном документе
В отчётах и печатных формах таблицей служит ТабличныйДокумент, и выделяют там не строки коллекции, а области ячеек. Строку целиком адресуют именем R5, диапазон строк — R5:R9, ячейку — R5C3.
Выделить область программно
&НаКлиенте
Процедура ВыделитьСтрокиОтчета(НомерС, НомерПо)
ИмяОбласти = СтрШаблон("R%1:R%2", Формат(НомерС, "ЧГ=0"), Формат(НомерПо, "ЧГ=0"));
Область = ТабДок.Область(ИмяОбласти);
Элементы.ТабДок.ТекущаяОбласть = Область;
КонецПроцедуры
Здесь ТабДок — реквизит формы типа ТабличныйДокумент, Элементы.ТабДок — поле на форме. Формат(…, "ЧГ=0") убирает пробел-разделитель групп разрядов: без него строка R1 250 для 1250-й строки даст ошибку адреса.
Обойти выделенные пользователем ячейки
Классическая задача — посчитать сумму выделенных ячеек, как это делает калькулятор в типовых отчётах. Пользователь может выделить несколько несмежных областей с Ctrl, поэтому перебираем коллекцию ВыделенныеОбласти:
&НаКлиенте
Функция СуммаВыделенныхЯчеек()
Сумма = 0;
Для Каждого Область Из ТабДок.ВыделенныеОбласти Цикл
Если ТипЗнч(Область) <> Тип("ОбластьЯчеекТабличногоДокумента") Тогда
Продолжить;
КонецЕсли;
Для НомСтроки = Область.Верх По Область.Низ Цикл
Для НомКолонки = Область.Лево По Область.Право Цикл
Ячейка = ТабДок.Область(НомСтроки, НомКолонки, НомСтроки, НомКолонки);
Значение = ЧислоИзТекста(Ячейка.Текст);
Сумма = Сумма + Значение;
КонецЦикла;
КонецЦикла;
КонецЦикла;
Возврат Сумма;
КонецФункции
&НаКлиенте
Функция ЧислоИзТекста(Текст)
ОписаниеЧисла = Новый ОписаниеТипов("Число");
Чистый = СтрЗаменить(СтрЗаменить(Текст, Символы.НПП, ""), " ", "");
Возврат ОписаниеЧисла.ПривестиЗначение(СтрЗаменить(Чистый, ",", "."));
КонецФункции
Если пользователь выделил строку целиком, у области Право может оказаться очень большим. Ограничьте обход фактической шириной документа: Мин(Область.Право, ТабДок.ШиринаТаблицы). То же для Низ и ВысотаТаблицы.
Подсветить строку цветом
Иногда «выделить» значит покрасить. В табличном документе это делается напрямую, без условного оформления:
Область = ТабДок.Область("R12");
Область.ЦветФона = Новый Цвет(255, 242, 204);
Область.Шрифт = Новый Шрифт(Область.Шрифт, , , Истина); // жирный
Красить лучше при формировании отчёта, на сервере, сразу после вывода нужной строки. Тогда номер строки известен точно: это ТабДок.ВысотаТаблицы сразу после ТабДок.Вывести(Область).
Подсветка строк через условное оформление
Для списков и табличных частей покраску задают условным оформлением. Оно проверяет условие на каждой строке и меняет цвет фона, текста или шрифт. Пользователь может выделять и прокручивать строки, подсветка остаётся.
Динамический список: подсветить документы с большой суммой
У динамического списка своё условное оформление, его поля — это поля запроса списка:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Элемент = Список.УсловноеОформление.Элементы.Добавить();
Отбор = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("СуммаДокумента");
Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Больше;
Отбор.ПравоеЗначение = 100000;
Элемент.Оформление.УстановитьЗначениеПараметра("ЦветФона", Новый Цвет(255, 235, 235));
Элемент.Представление = "Крупные суммы";
КонецПроцедуры
Поля оформляемых колонок не указаны, поэтому цвет ложится на всю строку. Поле в отборе должно быть в выборке запроса списка, иначе условие не сработает.
Табличная часть: подсветить отмеченные строки
Для табличной части используют условное оформление формы. Удобный приём — добавить колонку-флажок «Выбрана» (реквизит формы или реквизит табличной части) и красить строки по ней. Получается программное «выделение», которое переживает пересортировку и обновление.
&НаСервере
Процедура НастроитьПодсветкуВыбранных()
Элемент = УсловноеОформление.Элементы.Добавить();
Для Каждого Колонка Из Элементы.Товары.ПодчиненныеЭлементы Цикл
ПолеОформления = Элемент.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных(Колонка.Имя);
КонецЦикла;
Отбор = Элемент.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Отбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Объект.Товары.Выбрана");
Отбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Отбор.ПравоеЗначение = Истина;
Элемент.Оформление.УстановитьЗначениеПараметра("ЦветФона", Новый Цвет(226, 239, 218));
КонецПроцедуры
Если колонки объединены в группы, ПодчиненныеЭлементы вернёт группу, а не поле. Тогда обходите элементы рекурсивно или перечислите нужные колонки явно.
Условное оформление через код удобно, если правил больше одного-двух или они зависят от настроек. Простое правило быстрее задать в конфигураторе: свойство «Условное оформление» формы или динамического списка.
Выделение строк в табличной части и таблице значений на форме
В коллекциях формы (ДанныеФормыКоллекция) выделение работает через идентификаторы. Номер строки и индекс не подходят: после сортировки они меняются, а идентификатор остаётся.
&НаКлиенте
Процедура ВыделитьСтрокиБезЦены(Команда)
Элементы.Товары.ВыделенныеСтроки.Очистить();
ПерваяНайденная = Неопределено;
Для Каждого Строка Из Объект.Товары Цикл
Если Строка.Цена = 0 Тогда
Ид = Строка.ПолучитьИдентификатор();
Элементы.Товары.ВыделенныеСтроки.Добавить(Ид);
Если ПерваяНайденная = Неопределено Тогда
ПерваяНайденная = Ид;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если ПерваяНайденная <> Неопределено Тогда
Элементы.Товары.ТекущаяСтрока = ПерваяНайденная;
КонецЕсли;
КонецПроцедуры
Удаление выделенных строк делают в два прохода: сначала копируют идентификаторы в отдельный массив, потом удаляют. Если удалять, перебирая саму ВыделенныеСтроки, коллекция меняется под циклом и часть строк пропускается.
Идентификаторы = Новый Массив;
Для Каждого Ид Из Элементы.Товары.ВыделенныеСтроки Цикл
Идентификаторы.Добавить(Ид);
КонецЦикла;
Для Каждого Ид Из Идентификаторы Цикл
Строка = Объект.Товары.НайтиПоИдентификатору(Ид);
Если Строка <> Неопределено Тогда
Объект.Товары.Удалить(Строка);
КонецЕсли;
КонецЦикла;
Особенности выделения в дереве значений
Дерево на форме хранится как ДанныеФормыДерево, а строки устроены иерархически. Принцип тот же — идентификаторы, но есть два нюанса.
- Подчинённую строку не видно, пока свёрнут родитель. Перед установкой курсора разверните ветку методом
Развернуть(). Он доступен только на клиенте. - Обход дерева рекурсивный. Обычный
Для КаждогопоДерево.ПолучитьЭлементы()даст только верхний уровень.
Найти строку по значению, раскрыть путь к ней и выделить:
&НаКлиенте
Процедура НайтиИВыделить(Номенклатура)
Найденная = НайтиВВетке(Дерево.ПолучитьЭлементы(), Номенклатура);
Если Найденная = Неопределено Тогда
Возврат;
КонецЕсли;
Родитель = Найденная.ПолучитьРодителя();
Пока Родитель <> Неопределено Цикл
Элементы.Дерево.Развернуть(Родитель.ПолучитьИдентификатор());
Родитель = Родитель.ПолучитьРодителя();
КонецЦикла;
Ид = Найденная.ПолучитьИдентификатор();
Элементы.Дерево.ТекущаяСтрока = Ид;
Элементы.Дерево.ВыделенныеСтроки.Очистить();
Элементы.Дерево.ВыделенныеСтроки.Добавить(Ид);
КонецПроцедуры
&НаКлиенте
Функция НайтиВВетке(Элементы_, Значение)
Для Каждого Строка Из Элементы_ Цикл
Если Строка.Номенклатура = Значение Тогда
Возврат Строка;
КонецЕсли;
Вложенная = НайтиВВетке(Строка.ПолучитьЭлементы(), Значение);
Если Вложенная <> Неопределено Тогда
Возврат Вложенная;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Для отметки флажками в дереве часто добавляют колонку Пометка и при её изменении проставляют то же значение всем подчинённым элементам. Это делают в обработчике ПриИзменении колонки рекурсивной процедурой. Подсветку отмеченных строк настраивают так же, как для табличной части, с полем Дерево.Пометка в отборе.
Как реагировать на выделение строки: события таблицы
Чтобы при перемещении курсора что-то происходило — показывалась картинка товара, обновлялась сумма выделенного — используйте события элемента формы.
| Событие | Когда срабатывает | Типичное применение |
|---|---|---|
ПриАктивизацииСтроки | Курсор встал на другую строку | Обновить связанную панель, подчинённый список |
Выбор | Двойной щелчок или Enter на строке | Открыть свою форму вместо стандартной |
ПриАктивизации (поле табличного документа) | Изменилась выделенная область | Пересчитать сумму выделенных ячеек |
ПриАктивизацииСтроки срабатывает при каждом шаге стрелкой. Если в нём вызывать сервер, список начинает подтормаживать при пролистывании. Стандартный приём — отложить обработку через обработчик ожидания:
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
ОтключитьОбработчикОжидания("ОбновитьСвязанныеДанные");
ПодключитьОбработчикОжидания("ОбновитьСвязанныеДанные", 0.2, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСвязанныеДанные()
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ЗагрузитьКартинкуНаСервере(ТекущиеДанные.Ссылка);
КонецПроцедуры
Третий параметр Истина делает обработчик однократным. Пока пользователь быстро листает, вызов сдвигается, и сервер получает один запрос на итоговой строке.
В ОбновитьСвязанныеДанные же удобно выставлять отбор подчинённого динамического списка по ссылке из ТекущиеДанные: так строится связка «документы — их строки» на одной форме.
Ошибки при программном выделении строк и как их исправить
ВыделенныеСтроки передали номер строки или индекс. Нужен Строка.ПолучитьИдентификатор().ЗначениеВДанныеФормы, Загрузить), идентификаторы строк стали новыми. Запомните ключевые значения (например, номенклатуру) до перезаполнения и после него найдите строки заново.СтрокаГруппировкиДинамическогоСписка.Объект.Товары.Выбрана, а не Товары.Выбрана).Если код по выделенным строкам меняет данные (проводит, удаляет, перезаполняет документы), показывайте пользователю число затронутых строк и спрашивайте подтверждение через ПоказатьВопрос(). Случайный Ctrl+A перед массовой операцией — обычная история.
Частые вопросы
Можно ли выделить строки в динамическом списке на сервере?
ТекущаяСтрока можно в ПриСозданииНаСервере. Набор выделенных строк надёжнее менять на клиенте, после того как список отрисован.Как снять выделение со всех строк?
Элементы.Список.ВыделенныеСтроки.Очистить(). Курсор при этом остаётся на текущей строке.Как узнать, сколько строк выделил пользователь?
Элементы.Список.ВыделенныеСтроки.Количество(). Значение удобно выводить в заголовке колонки или в надписи на форме через обработчик ПриАктивизацииСтроки.Почему в обычной форме код с идентификаторами не работает?
ВыделенныеСтроки сами строки таблицы, а не идентификаторы. Код для управляемых и обычных форм пишут по-разному.