Как обновить родительскую форму списка после записи и закрытия дочерней формы в 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

В процессе работы с 1С часто возникает задача: из одной формы (например, списка документов) мы открываем другую (форму нового документа), выполняем в ней какие-то действия, а после ее закрытия нужно, чтобы исходная форма обновилась и отобразила эти изменения. Стандартный пример: в списке заказов есть колонка с флажком, который должен установиться после создания отгрузки по этому заказу. Если просто создать и закрыть отгрузку, флажок не появится до ручного обновления списка по кнопке F5. Рассмотрим, как автоматизировать этот процесс.

Проанализируем типичную ситуацию. Есть форма списка документов "Тип №1". Пользователь нажимает кнопку, которая программно создает документ "Тип №2" на основании текущей строки списка и открывает его форму. После того как пользователь записывает и закрывает документ "Тип №2", нам необходимо, чтобы в списке документов "Тип №1" отобразились изменения — например, появился флажок, что подчиненный документ создан. Для отображения подобных отметок пригодится способ вывода флажков в динамическом списке на управляемых формах.

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

Решение: Универсальный механизм Оповестить() и ОбработкаОповещения()

Наиболее правильным и гибким способом решения этой задачи является использование встроенного в платформу механизма обмена сообщениями между формами. Он состоит из двух частей: метода Оповестить(), который отправляет сообщение, и процедуры-обработчика ОбработкаОповещения(), которая это сообщение принимает.

Этот подход хорош тем, что он не зависит от типов данных и связей между формами. Мы можем отправить сообщение из любой формы в любую другую открытую форму, которая "умеет" его обрабатывать. Разберем реализацию по шагам.

Шаг 1. Отправка оповещения из дочерней формы

В форме, из которой нужно отправить сигнал об изменениях (в нашем примере — форма документа "Тип №2"), необходимо вызвать метод Оповестить(). Лучше всего это делать в обработчиках событий, которые гарантированно срабатывают после сохранения данных, например, ПослеЗаписи или ПередЗакрытием (если важно, чтобы форма уже была закрыта).

Пример кода для формы документа "Тип №2":


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Оповещаем другие формы о том, что был создан новый документ.
    // "ОбновитьСписокОснований" - это уникальное имя нашего события.
    // Вторым параметром можно передать любые данные, например, ссылку на новый документ.
    Оповестить("ОбновитьСписокОснований", Объект.Ссылка, ЭтаФорма);
КонецПроцедуры

Что здесь происходит:

  1. Оповестить() — глобальный метод, доступный в любом клиентском модуле.
  2. "ОбновитьСписокОснований" — это имя события. Вы можете придумать любое уникальное имя. По нему форма-приемник будет понимать, что это сообщение адресовано именно ей.
  3. Объект.Ссылка — это необязательный параметр, с помощью которого можно передать дополнительную информацию. Например, форма-получатель сможет не просто обновиться, а найти и спозиционироваться на строке, связанной с этим объектом.
  4. ЭтаФорма — третий параметр, источник события.

Шаг 2. Прием и обработка оповещения в родительской форме

Теперь в форме, которая должна отреагировать на событие (в нашем примере — форма списка документов "Тип №1"), нужно создать экспортную процедуру-обработчик с предопределенным платформой именем ОбработкаОповещения.

Пример кода для формы списка документов "Тип №1":


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

Разберем этот код подробнее:

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

Важный момент: не путайте метод Обновить() с методом ОбновитьОтображениеДанных(). Последний лишь перерисовывает уже загруженные данные в списке (например, для условного оформления) и не делает нового запроса к базе данных. В нашей задаче он не поможет.

Альтернативные подходы и почему они не подошли

В ходе обсуждения на форуме упоминались и другие механизмы, которые стоит рассмотреть для полного понимания картины.

Метод ОповеститьОбИзменении()

Этот метод очень похож по названию, но работает принципиально иначе. ОповеститьОбИзменении(СсылкаНаОбъект) сообщает всем формам, которые отображают данные этого же типа, что данные изменились.

Например, если бы у нас было открыто два списка документов "Тип №2", и в одном из них мы бы создали новый документ, то после вызова ОповеститьОбИзменении() второй список бы автоматически обновился.

В нашем случае этот метод не подходит, потому что мы меняем данные объекта "Тип №2", а обновить нужно форму списка объектов "Тип №1". Эти формы отображают разные данные, поэтому оповещение не будет обработано.

Обработчик закрытия в методе ОткрытьФорму()

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

Пример кода для формы списка "Тип №1":


&НаКлиенте
Процедура СоздатьДокументПоОснованию()
    
    // ... здесь код подготовки параметров для открытия формы ...
    ПараметрыФормы = Новый Структура;
    
    // Создаем описание оповещения. 
    // "ПослеЗакрытияДочернейФормы" - имя процедуры, которая будет вызвана.
    // ЭтотОбъект - модуль, в котором находится эта процедура.
    Оповещение = Новый ОписаниеОповещения("ПослеЗакрытияДочернейФормы", ЭтотОбъект);

    // Открываем форму и передаем оповещение в качестве последнего параметра
    ОткрытьФорму("Документ.ПоступлениеСырья.ФормаОбъекта", ПараметрыФормы, ЭтаФорма,,,, Оповещение);

КонецПроцедуры


&НаКлиенте
// Процедура, которая будет вызвана после закрытия дочерней формы
Процедура ПослеЗакрытияДочернейФормы(РезультатЗакрытия, ДополнительныеПараметры) Экспорт

    // Независимо от того, как закрыли форму (сохранили или отменили), обновляем список
    Элементы.Список.Обновить();

КонецПроцедуры

Этот способ тоже отлично решает задачу. Его отличие от связки Оповестить()/ОбработкаОповещения() в том, что обновление жестко привязано к моменту закрытия дочерней формы. Первый же способ более гибкий, так как позволяет отправить сигнал в любой момент, например, сразу после записи, даже если пользователь продолжит работать с формой.

В итоге, для межформенного взаимодействия, особенно когда формы отображают разные по своей сути данные, связка Оповестить() и ОбработкаОповещения() является наиболее универсальным, гибким и рекомендуемым решением.

← На главную