Как программно управлять динамическим списком формы: запрос, отборы, параметры и перечитывание
Содержание
Чтобы программно обновить динамический список
в 1С 8.3, на клиенте вызовите Элементы.Список.Обновить(). Если данные поменялись в другой форме или в серверной процедуре, удобнее оповестить все открытые списки сразу: ОповеститьОбИзменении(Ссылка) или ОповеститьОбИзменении(Тип("СправочникСсылка.Номенклатура")). Отборы, параметры и порядок меняются через свойства реквизита Список и его КомпоновщикНастроек, и после такой смены список перечитывается сам.
Это материал для разработчика: всё ниже относится к платформе 8.3 и управляемым формам, от конфигурации не зависит. Примеры кода написаны для реквизита формы с именем Список и одноимённой таблицы формы на нём.
Что внутри динамического списка и почему он «живёт» по правилам СКД
Динамический список — это не таблица значений. Он не держит данные в форме, а читает их из базы порциями: ровно столько строк, сколько видно на экране, плюс небольшой запас. За это отвечает свойство ДинамическоеСчитываниеДанных, по умолчанию оно включено.
Под капотом у списка движок системы компоновки данных. Отсюда три следствия, которые определяют весь программный код:
- 🧩 отбор, порядок и условное оформление — это объекты СКД:
ОтборКомпоновкиДанных,ПорядокКомпоновкиДанных,УсловноеОформлениеКомпоновкиДанных; - 🔑 параметры запроса задаются как параметры компоновки, а не как у обычного объекта
Запрос; - ⚙️ всеми настройками владеет
КомпоновщикНастроекс тремя слоями: настройки разработчика, пользовательские и фиксированные.
Два режима: основная таблица и произвольный запрос
Список работает либо по основной таблице (ОсновнаяТаблица, например Справочник.Номенклатура), либо по своему запросу. Во втором случае включён флаг ПроизвольныйЗапрос, а текст лежит в ТекстЗапроса.
| Режим | Когда выбирать | Что учесть |
|---|---|---|
| Основная таблица | Обычный список справочника или документа | Быстрее всего, доступны все стандартные команды |
| Произвольный запрос с основной таблицей | Нужны поля из связанных таблиц, а строка списка остаётся объектом | Ключ строки — ссылка основной таблицы, команды формы работают |
| Произвольный запрос без основной таблицы | Свод, группировки, данные регистров | Нет стандартного открытия объекта, нужен ключ строки |
Какие поля список реально читает
Список запрашивает из базы только те поля, которые выведены на форму. Поле есть в запросе, но не добавлено в таблицу формы — в данных строки его не будет. Попытка прочитать Элементы.Список.ТекущиеДанные.Склад вернёт ошибку обращения к полю.
Это один из главных приёмов при работе со списком: поле, которое нужно коду, но не нужно пользователю, помечают «Использовать всегда». В конфигураторе это галочка в окне настройки полей списка, в коде — метод УстановитьОбязательноеИспользование:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Список.УстановитьОбязательноеИспользование("Склад", Истина);
Список.УстановитьОбязательноеИспользование("Ответственный", Истина);
КонецПроцедуры
Как изменить состав полей и текст запроса из кода
Состав колонок динамического списка меняют двумя путями. Простой — переписать ТекстЗапроса и добавить на форму элемент под новое поле. Мягкий — оставить запрос как есть и просто скрыть или показать уже существующие колонки через Видимость.
Смена текста запроса на сервере
Делайте это в ПриСозданииНаСервере или в серверной процедуре, которую вызывает команда. Пример: в список номенклатуры добавляем цену из регистра сведений.
&НаСервере
Процедура ДобавитьКолонкуЦеныНаСервере()
Список.ПроизвольныйЗапрос = Истина;
Список.ОсновнаяТаблица = "Справочник.Номенклатура";
Список.ТекстЗапроса =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул,
| ЕСТЬNULL(Цены.Цена, 0) КАК Цена
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаЦен, ) КАК Цены
| ПО Номенклатура.Ссылка = Цены.Номенклатура";
Список.Параметры.УстановитьЗначениеПараметра("ДатаЦен", ТекущаяДатаСеанса());
Если Элементы.Найти("СписокЦена") = Неопределено Тогда
НовоеПоле = Элементы.Добавить("СписокЦена", Тип("ПолеФормы"), Элементы.Список);
НовоеПоле.Вид = ВидПоляФормы.ПолеВвода;
НовоеПоле.ПутьКДанным = "Список.Цена";
КонецЕсли;
КонецПроцедуры
Имя регистра и его измерения здесь условные: подставьте те, что есть в вашей конфигурации. Принцип не меняется: новый текст запроса, значения всех параметров, затем элемент формы с ПутьКДанным = "Список.<ИмяПоля>".
После смены ТекстЗапроса пользовательские настройки списка (его отборы, сортировка, ширина колонок) могут не подойти к новому набору полей. Отборы по исчезнувшим полям перестанут работать. Меняйте запрос до того, как форма загрузит пользовательские настройки, то есть в ПриСозданииНаСервере.
Скрыть колонку без смены запроса
Если поле в запросе уже есть, гораздо дешевле управлять видимостью:
Элементы.СписокАртикул.Видимость = ПоказыватьАртикул;
Помните про правило из прошлого раздела: скрытое поле перестаёт читаться. Если код на него опирается, включите для него обязательное использование.
Отборы динамического списка из кода
Самый частый вопрос — куда ставить отбор. Слоёв два, и выбор зависит от того, должен ли пользователь его видеть и снимать.
| Где | Видит пользователь | Может снять | Для чего |
|---|---|---|---|
Список.Отбор (фиксированные настройки) | Нет | Нет | Жёсткое ограничение формы: «только документы этой организации» |
Список.КомпоновщикНастроек.Настройки.Отбор | Да, если режим отображения не «Недоступный» | Да | Отбор по умолчанию, который можно поменять |
| Пользовательские настройки | Да | Да | Отборы, которые пользователь поставил сам и сохранил |
Фиксированный отбор
&НаСервере
Процедура УстановитьОтборПоОрганизации(Организация)
Список.Отбор.Элементы.Очистить();
ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Организация");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Организация;
ЭлементОтбора.Использование = Истина;
КонецПроцедуры
Очистить() удаляет все фиксированные отборы, включая чужие. Если на форме их ставит не только ваш код, ищите свой элемент по ЛевоеЗначение и меняйте только его ПравоеЗначение и Использование.
💡 В конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11 и другие) есть готовая процедура ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка. Она сама находит элемент по имени поля, обновляет его или добавляет новый. Писать отбор вручную в таких базах незачем.
Группа «ИЛИ»
Нужно показать документы, где ответственный — текущий пользователь, или автор — текущий пользователь:
Группа = Список.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
Группа.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
Группа.Использование = Истина;
Для Каждого ИмяПоля Из СтрРазделить("Ответственный,Автор", ",") Цикл
Элемент = Группа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(ИмяПоля);
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Элемент.ПравоеЗначение = ПараметрыСеанса.ТекущийПользователь;
Элемент.Использование = Истина;
КонецЦикла;
Параметр сеанса ТекущийПользователь есть в типовых конфигурациях на БСП. В своей разработке подставьте свой способ получить пользователя.
Отбор, который видит пользователь
Чтобы отбор появился в панели быстрого отбора, добавьте его в КомпоновщикНастроек.Настройки.Отбор и задайте режим отображения РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ. Учтите: сохранённые пользовательские настройки при следующем открытии формы перекроют то, что вы поставили в коде.
Порядок строк
Сортировка задаётся так же, через объекты СКД:
Список.Порядок.Элементы.Очистить();
ЭлементПорядка = Список.Порядок.Элементы.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
ЭлементПорядка.Поле = Новый ПолеКомпоновкиДанных("Дата");
ЭлементПорядка.ТипУпорядочивания = НаправлениеСортировкиКомпоновкиДанных.Убыв;
ЭлементПорядка.Использование = Истина;
Условное оформление строк
Выделить цветом строки, например непроведённые документы, тоже можно из кода. Оформление — ещё один объект СКД внутри списка:
ЭлементОформления = Список.УсловноеОформление.Элементы.Добавить();
Условие = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Условие.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Проведен");
Условие.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Условие.ПравоеЗначение = Ложь;
Условие.Использование = Истина;
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Серый);
ЭлементОформления.Использование = Истина;
Поле в условии должно читаться списком. Если колонки «Проведен» на форме нет, включите для неё обязательное использование, иначе оформление не сработает.
Параметры запроса динамического списка
Если в ТекстЗапроса есть &Параметр, значение ему задают через коллекцию Параметры списка. Метод тот же, что у компоновщика:
Список.Параметры.УстановитьЗначениеПараметра("ДатаЦен", Объект.Дата);
Список.Параметры.УстановитьЗначениеПараметра("Склады", МассивСкладов);
Несколько практических правил:
- Задавайте значения всех параметров в
ПриСозданииНаСервере. Иначе первое чтение списка пойдёт с незаполненным параметром. - Для условия
В (&Склады)передавайте массив или список значений, а не одну ссылку, если складов может быть несколько. - Смена значения параметра на клиенте, например в обработчике
ПриИзмененииполя отбора на форме, сразу перечитывает список. Отдельный вызовОбновить()не нужен. - Если список строится по основной таблице без произвольного запроса, параметров у него нет. Ограничивайте выборку отбором.
Параметр или отбор: что выбрать
Отбор накладывается на итог запроса и виден в настройках компоновки. Параметр же работает внутри текста запроса: в условиях соединений, в виртуальных таблицах регистров (СрезПоследних(&Дата), Остатки(&Период)), в вычисляемых полях. Всё, что должно попасть внутрь виртуальной таблицы, — только параметром.
Как обновить динамический список программно, когда данные поменялись
Список сам перечитывается, когда пользователь листает его, меняет отбор или сортировку, а также когда объект записан в его стандартной форме. А вот изменения, внесённые кодом в обход формы, список не видит. Типичный пример: команда вызывает сервер, там в цикле перепроводятся документы, а строки на экране остаются старыми.
Способ 1. Обновить() на таблице формы
Метод вызывают на клиенте после возврата из серверной процедуры:
&НаКлиенте
Процедура Перепровести(Команда)
ВыделенныеСтроки = Элементы.Список.ВыделенныеСтроки;
Если ВыделенныеСтроки.Количество() = 0 Тогда
Возврат;
КонецЕсли;
ПерепровестиНаСервере(ВыделенныеСтроки);
Элементы.Список.Обновить();
КонецПроцедуры
Обновляется только эта таблица формы. Другие открытые окна с тем же списком об изменениях не узнают.
Способ 2. ОповеститьОбИзменении
Глобальный метод, который сообщает всем динамическим спискам в сеансе пользователя: данные такого-то объекта или типа изменились. Каждый открытый список, где встречаются эти данные, перечитается сам.
// Одна ссылка
ОповеститьОбИзменении(ДокументСсылка);
// Все объекты типа
ОповеститьОбИзменении(Тип("ДокументСсылка.РеализацияТоваровУслуг"));
// Регистр сведений
ОповеститьОбИзменении(Тип("РегистрСведенийКлючЗаписи.ЦеныНоменклатуры"));
Метод клиентский. Из серверной процедуры его вызвать нельзя: верните с сервера массив изменённых ссылок и оповестите в цикле на клиенте. Изменений много — одного оповещения по типу хватит.
Способ 3. Оповестить и ОбработкаОповещения
Если перечитать нужно не просто список, а ещё и связанные данные формы (итоги в подвале, надпись с количеством), используйте пару Оповестить("ИмяСобытия", Параметр) и обработчик ОбработкаОповещения в нужной форме. Внутри обработчика проверьте имя события и вызовите Элементы.Список.Обновить().
| Способ | Что обновляет | Когда брать |
|---|---|---|
Элементы.Список.Обновить() | Одну таблицу в текущей форме | Команда этой же формы изменила данные |
ОповеститьОбИзменении | Все открытые списки с этими данными | Данные поменяли в другой форме, обработке, при загрузке |
Оповестить + ОбработкаОповещения | То, что вы запрограммировали в обработчике | Нужно перестроить ещё что-то кроме строк |
Обновление по таймеру
Для списков, которые меняют другие пользователи (очередь заказов, журнал обмена), у динамического списка есть свойства автообновления и периода в секундах — их видно в палитре свойств реквизита. Если логику нужно контролировать самому, подключите обработчик ожидания:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ОбновитьСписокПоТаймеру", 60);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСписокПоТаймеру()
Элементы.Список.Обновить();
КонецПроцедуры
Каждый такт — это запрос к базе от каждого открытого окна. Интервал в 5 секунд на списке с тяжёлым запросом и 30 пользователях заметно нагрузит сервер. Обычно хватает 30–60 секунд.
Частые ошибки при работе со списком из кода
Ещё одна ловушка — вызвать Элементы.Список.Обновить() сразу после установки отбора или параметра. Список и так перечитается, а лишний вызов даст второй запрос к базе.