Разработка и Конфигурация · 14 мин
Как программно обновить динамический список в 1С
Как программно обновить динамический список в 1С: метод Обновить(), оповещения форм, Элементы.Список.Обновить, обработчики событий и типичные ошибки разработчика.
Динамический список — основной механизм отображения данных в управляемых формах 1С: списки документов, справочников, регистров сведений строятся именно на нём. После программного изменения данных — записи документа из обработки, изменения регистра, загрузки из внешнего источника — список на экране не перечитывается сам, и пользователь видит устаревшие строки. Чтобы интерфейс показал актуальное состояние, список нужно обновить программно.
У задачи есть несколько решений, и выбор зависит от контекста: обновление инициируется из той же формы, из другой формы, из фонового задания или после записи объекта. Универсального «одного правильного» способа нет — метод Обновить() у элемента формы, оповещения через Оповестить() и обработчик ОбработкаОповещения решают разные сценарии.
Ниже разобраны рабочие приёмы с кодом на встроенном языке, условия их применения, ограничения платформы и ошибки, из-за которых обновление «не срабатывает», хотя код выполняется без исключений.
Как устроено обновление динамического списка и почему оно не происходит само
Динамический список выполняет запрос к базе данных и кэширует результат в порциях — платформа не держит в памяти всю выборку, а подгружает строки по мере прокрутки. Повторное выполнение запроса — относительно дорогая операция, поэтому платформа не перечитывает список при каждом изменении данных в информационной базе.
Частично платформа помогает сама: если объект записан интерактивно из своей формы, открытые списки соответствующего типа обычно обновляются автоматически благодаря штатному оповещению об изменении. Но при программной записи из обработки, при изменении данных запросом напрямую, при работе фонового задания или при изменении данных другим сеансом автоматического обновления не происходит — его нужно инициировать явно.
Важно понимать разницу между обновлением данных и обновлением отображения. Первое перечитывает выборку из базы, второе лишь перерисовывает элементы формы. Путаница между ними — частая причина ситуации, когда «код отработал, а список старый».
Обновление списка из той же формы: Элементы.Список.Обновить()
Самый прямой сценарий — код выполняется в модуле той же управляемой формы, где расположен список. У элемента формы типа «Таблица», связанного с динамическим списком, есть метод Обновить(), который заставляет список перечитать данные:
&НаКлиенте
Процедура ПослеЗагрузкиДанных(Команда)
ВыполнитьЗагрузкуНаСервере();
// Перечитываем данные динамического списка
Элементы.Список.Обновить();
КонецПроцедуры
Здесь Список — имя элемента формы (таблицы), а не реквизита. Если таблица на форме названа иначе, например СписокДокументов, обращаться нужно по этому имени: Элементы.СписокДокументов.Обновить().
Если изменились не сами данные списка, а связанные реквизиты формы, от которых зависит отображение (например, условное оформление или вычисляемые колонки), достаточно более лёгкого вызова:
ЭтаФорма.ОбновитьОтображениеДанных();
Этот метод перерисовывает форму без повторного выполнения запроса к базе, поэтому работает быстрее, но не подтягивает новые строки.
Обновление списка из другой формы через оповещения
Когда данные меняет одна форма (например, форма обработки или форма документа), а список открыт в другом окне, прямого доступа к его элементам нет. Стандартный механизм взаимодействия форм — оповещения.
ОповеститьОбИзменении после записи объекта
Если программно записан конкретный объект, правильный способ сообщить об этом системе — метод ОповеститьОбИзменении():
&НаКлиенте
Процедура ЗаписатьИОбновить(Команда)
НоваяСсылка = ЗаписатьДокументНаСервере();
// Уведомляем все открытые формы об изменении объекта
ОповеститьОбИзменении(НоваяСсылка);
КонецПроцедуры
Открытые динамические списки, содержащие объекты этого типа, отреагируют на оповещение и обновятся. Дополнительно можно передать тип изменения через ОповеститьОбИзменении(Ссылка, ТипИзменения), но в большинстве случаев достаточно базового вызова.
Произвольное оповещение с обработчиком на форме списка
Если нужно обновить конкретную форму списка или передать параметры, используется пара Оповестить() — ОбработкаОповещения. На форме-источнике:
&НаКлиенте
Процедура ПослеЗагрузки(Команда)
ВыполнитьЗагрузкуНаСервере();
Оповестить("ОбновитьСписокЗаказов");
КонецПроцедуры
На форме списка добавляется обработчик:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ОбновитьСписокЗаказов" Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Имя события — произвольная строка, но она должна совпадать у отправителя и получателя. Чтобы не ловить чужие оповещения, имена событий принято делать уникальными, например с префиксом конфигурации или подсистемы.
- 1Определите тип сценария: изменён конкретный объект или нужно произвольное событие
- 2Для объекта вызовите ОповеститьОбИзменении(Ссылка) после записи
- 3Для произвольного события отправьте Оповестить("ИмяСобытия") из формы-источника
- 4На форме списка добавьте ОбработкаОповещения с проверкой ИмяСобытия
- 5Внутри обработчика вызовите Элементы.Список.Обновить()
- 6Проверьте: откройте обе формы, выполните действие, убедитесь, что строки перечитались
Обновление после записи объекта и стандартные события формы
Если список должен реагировать на запись объектов определённого типа независимо от того, откуда выполнена запись, на форме списка используется обработчик ОбработкаЗаписиНового для новых объектов и уже упомянутый ОбработкаОповещения для штатных оповещений платформы.
Типовой приём — реагировать на стандартное событие записи, которое платформа рассылает при интерактивной записи из формы объекта:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "Запись_РеализацияТоваровУслуг" Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Имя такого события формируется по шаблону «Запись_» + имя типа объекта, как оно задано в конфигураторе. Этот приём полезен, когда список показывает данные, зависящие от записываемого документа, но сам документ в списке напрямую не отображается.
Для регистров сведений, подчинённых регистратору, и для списков с произвольным запросом штатное оповещение об изменении объекта может не приводить к обновлению — платформа не всегда связывает записанный объект с источником данных списка. В таких случаях надёжнее собственное именованное оповещение.
Особые случаи: фоновые задания, периодическое обновление и произвольный запрос
Обновление после фонового задания
Фоновое задание выполняется на сервере и не имеет доступа к клиентскому контексту. После его завершения управление возвращается в клиентскую процедуру (например, через механизм длительных операций БСП или ожидание завершения), и именно там вызывается Элементы.Список.Обновить(). Пытаться «пробросить» обновление изнутри фонового задания бессмысленно — у него нет формы.
Периодическое обновление по таймеру
Для мониторинговых списков (очереди задач, журналы обмена) применяют ПодключитьОбработчикОжидания():
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ОбновитьСписокПоТаймеру", 60);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСписокПоТаймеру()
Элементы.Список.Обновить();
КонецПроцедуры
Интервал указывается в секундах. Слишком частое обновление (каждые несколько секунд) создаёт лишнюю нагрузку на сервер и сетевой трафик, особенно при тяжёлом запросе списка — интервал стоит выбирать исходя из реальной потребности в актуальности.
Список с произвольным запросом
Если у динамического списка включён флаг «Произвольный запрос» и установлена основная таблица, автоматическое обновление при записи объектов этой таблицы обычно работает. Если основная таблица не указана или запрос соединяет несколько источников, платформа не может определить, что именно перечитывать, — рассчитывать нужно только на явный вызов Обновить() или оповещения.
Как выбрать способ обновления
Почему обновление не срабатывает: разбор типичных ошибок
Наиболее частая причина — вызов Элементы.Список.Обновить() в серверной процедуре. Код компилируется не всегда, а если контекст позволяет, то выполняется без видимого эффекта, потому что элементы формы существуют только на клиенте. Проверка проста: директива компиляции процедуры должна быть &НаКлиенте.
Вторая группа ошибок — несовпадение имён. Имя события в Оповестить() и в ОбработкаОповещения отличается регистром букв или лишним символом; имя элемента формы перепутано с именем реквизита списка. Такие ошибки не вызывают исключений — оповещение просто уходит «в никуда».
Третья типичная ситуация — обновление вызывается до того, как данные реально записаны. Например, Обновить() вызван сразу после запуска серверной процедуры записи, не дождавшись её завершения, либо запись завершилась откатом транзакции. Список честно перечитывает базу — и видит старые данные.
Как проверить результат и снизить нагрузку
Проверка выполняется опытным путём: откройте форму списка и форму-источник изменений рядом, выполните действие (запись, загрузку, обработку) и убедитесь, что новые или изменённые строки появились в списке без его переоткрытия. Если список с отбором — проверьте, что обновлённая строка не «выпала» из отбора или, наоборот, корректно в него попала.
Для отладки оповещений полезно временно добавить в ОбработкаОповещения вывод имени события через Сообщить() — так видно, какие события реально приходят на форму и с какими параметрами.
По нагрузке: каждый вызов Обновить() — это повторное выполнение запроса списка. На списках с тяжёлым произвольным запросом и большим числом пользователей не стоит рассылать широковещательные оповещения после каждой мелкой записи. Разумный компромисс — обновлять по таймеру с умеренным интервалом или только по явному действию пользователя кнопкой «Обновить» (стандартная команда Обновить у таблицы динамического списка доступна и без программирования).
Как проверить результат
Новые или изменённые строки появились без переоткрытия формы
Список показывает старые данные до ручного обновления
Отладка или вывод сообщения показывает вход в ОбработкаОповещения с нужным событием
Обработчик не вызывается — событие не доходит или имя не совпадает
Вызов находится в клиентской процедуре после завершения записи
Вызов на сервере или до фиксации транзакции записи
Интервал таймера соответствует потребности, интерфейс не подтормаживает
Список мерцает, запросы выполняются каждые несколько секунд
Частые вопросы
Чем отличается Элементы.Список.Обновить() от ЭтаФорма.ОбновитьОтображениеДанных()?
Первый метод заставляет динамический список повторно выполнить запрос к базе и перечитать строки. Второй только перерисовывает элементы формы на основании уже имеющихся данных — новые записи из базы он не подтянет. Для актуализации содержимого списка нужен именно Обновить().
Можно ли обновить динамический список из серверной процедуры?
Напрямую — нет: элементы формы существуют только в клиентском контексте. Правильный шаблон — выполнить изменение данных в серверной процедуре, вернуть управление в клиентскую и там вызвать Элементы.Список.Обновить().
Почему список не обновляется после ОповеститьОбИзменении()?
Чаще всего дело в произвольном запросе списка без указанной основной таблицы — платформа не связывает оповещённый объект с источником данных. Также проверьте, что запись объекта действительно завершилась успешно и оповещение вызвано после записи, а не до неё. В таких случаях надёжнее собственное оповещение с обработчиком ОбработкаОповещения.
Как обновить список, если данные изменил другой пользователь или регламентное задание?
Между сеансами оповещения форм не работают. Варианты: периодическое обновление через ПодключитьОбработчикОжидания, обновление по кнопке пользователя либо внешние механизмы взаимодействия сеансов, если они предусмотрены используемой библиотекой. Для большинства сценариев достаточно таймера с интервалом в десятки секунд.
Сбрасывается ли текущая позиция и отбор списка при программном обновлении?
Установленные отбор и настройки списка при Обновить() сохраняются. Текущая строка обычно удерживается, если запись всё ещё входит в выборку; если она перестала удовлетворять отбору, позиция сместится. Это стоит учитывать при частых автоматических обновлениях — пользователь может потерять выделенную строку.
Нужно ли обновлять список после интерактивной записи документа из его формы?
Как правило, нет — при записи из собственной формы объекта платформа сама рассылает оповещение, и открытые списки этого типа обновляются автоматически. Программное обновление требуется при записи из обработок, фоновых заданий, обменов и других нестандартных источников изменения данных.
Что важно запомнить
Обновление динамического списка всегда выполняется на клиенте: Элементы.Список.Обновить() — из той же формы, ОповеститьОбИзменении() или пара Оповестить/ОбработкаОповещения — из других форм, обработчик ожидания — для периодической актуализации. Вызывайте обновление только после фактической записи данных и проверяйте совпадение имён событий и элементов формы.