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