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