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