Как программно открыть форму, изменить данные и обновить владельца при закрытии?

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

При разработке на платформе 1С:Предприятие 8.3 в режиме управляемого приложения часто возникает задача: необходимо открыть вспомогательную форму (например, для ввода комментария, выбора значения или редактирования связанного объекта), а после того как пользователь закроет эту форму, выполнить определенные действия в основной форме (например, обновить данные). Рассмотрим подробно, как правильно реализовать этот механизм, избегая использования устаревших модальных окон и глобальных оповещений.

Постановка задачи и анализ проблемы

Рассмотрим ситуацию, с которой столкнулся автор темы. Задача состоит в следующем:

  1. Из основной формы документа нужно программно открыть другую форму (в данном случае — форму редактирования комментария).
  2. В открытой форме пользователь меняет данные.
  3. При закрытии этой вспомогательной формы основная форма должна узнать об этом и обновить свои данные (перечитать объект).

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

Более правильным и современным подходом в 1С является использование механизма Описания Оповещения (Callback). Это позволяет четко связать событие закрытия конкретной формы с конкретной процедурой в вызывающей форме.

Реализация через ОписаниеОповещения

Разберем по шагам итоговое решение, к которому пришли в ходе обсуждения. Мы будем использовать конструктор Новый ОписаниеОповещения и метод ОткрытьФорму с соответствующими параметрами.

Весь код, приведенный ниже, выполняется в контексте клиента (&НаКлиенте), так как работа с окнами — это интерфейсная задача.

Шаг 1. Подготовка параметров и оповещения

В процедуре, которая инициирует открытие формы (например, по нажатию кнопки "Добавить комментарий"), нам нужно сделать три вещи:

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

Посмотрим на пример кода:


&НаКлиенте
Процедура мДобавитьКомментарий(Команда)     
    
    // 1. Формируем параметры. Передаем ссылку на текущий объект.
    // Ключ - это стандартный параметр, указывающий на объект данных формы.
    ПараметрыФормы = Новый Структура("Ключ", Объект.Ссылка);
    
    // 2. Создаем описание оповещения.
    // "мОбновитьФорму" - имя экспортной процедуры в ЭТОМ модуле, которая сработает после закрытия.
    // ЭтотОбъект - контекст формы, где находится процедура.
    Оповещение = Новый ОписаниеОповещения("мОбновитьФорму", ЭтотОбъект);
    
    // 3. Открываем форму.
    // Обратите внимание на параметры:
    // Параметр 3 (Владелец): ЭтотОбъект - связываем формы.
    // Параметр 6 (ОписаниеОповещенияОЗакрытии): передаем наше Оповещение.
    // Параметр 7 (РежимОткрытияОкна): БлокироватьОкноВладельца.
    ОткрытьФорму(
        "Документ.ПеремещениеТоваров.Форма.ФормаДокументаКомментарий", 
        ПараметрыФормы, 
        ЭтотОбъект, 
        ,,,
        Оповещение, 
        РежимОткрытияОкнаФормы.БлокироватьОкноВладельца
    );
    
КонецПроцедуры

Важный момент: Использование РежимОткрытияОкнаФормы.БлокироватьОкноВладельца создает эффект модальности, но не блокирует весь интерфейс программы. Пользователь не сможет вернуться к основной форме документа, пока не закроет форму комментария, но сможет переключиться на другие отчеты или документы. Это обеспечивает целостность логики работы.

Шаг 2. Обработка результата закрытия

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

В данной задаче мы предполагаем, что вспомогательная форма сама записала данные в базу данных. Поэтому задача основной формы — просто перечитать эти данные, чтобы отобразить актуальное состояние.


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

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

Альтернативный вариант: Передача значения без записи в БД

Иногда нам не нужно, чтобы вспомогательная форма записывала данные в базу. Мы хотим просто получить введенное пользователем значение (строку комментария) и поместить его в реквизит основной формы, а запись документа произойдет позже, когда пользователь нажмет "Записать" в основной форме.

Для этого изменим логику:

1. Во вспомогательной форме при закрытии (например, по кнопке "ОК") мы должны явно вернуть значение:


&НаКлиенте
Процедура КомандаОК(Команда)
    // Закрываем форму и передаем введенный текст как результат
    ЭтаФорма.Закрыть(ТекстКомментария); 
КонецПроцедуры

2. В основной форме, в процедуре обработки оповещения, мы анализируем РезультатЗакрытия:


&НаКлиенте
Процедура мОбновитьФорму(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    
    // Проверяем, что результат определен (пользователь не просто нажал крестик, а подтвердил выбор)
    Если РезультатЗакрытия <> Неопределено Тогда
        // Присваиваем полученное значение реквизиту объекта
        Объект.Комментарий = РезультатЗакрытия;
        
        // Помечаем форму как модифицированную ("звездочка" в заголовке)
        ЭтаФорма.Модифицированность = Истина;
    КонецЕсли;
    
КонецПроцедуры

Почему не сработал метод Оповестить()?

В исходном вопросе автор пытался использовать конструкцию Оповестить("ИзмененКомментарий"). Разберем, почему это вызывало трудности:

  1. Метод Оповестить() отправляет сообщение всем открытым формам. Чтобы его поймать, в форме-получателе должна быть определена процедура ОбработкаОповещения.
  2. Если форма открыта в режиме блокировки владельца, механизмы доставки оповещений могут вести себя специфически в зависимости от того, когда именно срабатывает оповещение (до закрытия окна или в процессе).
  3. Частая ошибка — использование типовой процедуры ОбработкаОповещения, где уже написано много кода (как в примере из темы, сообщение 5). Если добавить свой код не в то место условия Если...ИначеЕсли, он может просто не выполниться.

Использование связки ОписаниеОповещения + ОткрытьФорму является более "чистым" архитектурным решением для диалога между двумя конкретными формами.

Дополнительные возможности и советы

При использовании метода ОткрытьФорму полезно знать о других параметрах, которые могут упростить код:

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

Они работают по тому же принципу ОписаниеОповещения, но требуют написания меньшего количества кода.

Итог

Для решения задачи обновления родительской формы после работы с дочерней формой в 1С 8.3:

  1. Используйте конструктор Новый ОписаниеОповещения для указания процедуры-обработчика результата.
  2. Открывайте форму методом ОткрытьФорму, передавая туда оповещение и устанавливая режим БлокироватьОкноВладельца.
  3. В процедуре обработки обновляйте данные методом ЭтаФорма.Прочитать() (если данные записаны в БД) или обрабатывайте РезультатЗакрытия (если данные переданы напрямую).
← На главную