При разработке сложных интерфейсных решений в платформе 1С:Предприятие 7.7 нередко возникает задача организации взаимодействия двух и более форм. Классический сценарий: из основной обработки открывается подчиненная форма для подбора или уточнения данных, пользователь производит выбор, и результат должен вернуться в исходное окно. При модальном открытии (команда ОткрытьФормуМодально()) выполнение кода в вызывающей форме приостанавливается, и результат легко вернуть через параметр закрытия. Однако модальный режим блокирует всю систему, что не всегда удобно пользователю.
При переходе к немодальным формам разработчики сталкиваются с вопросом: как передавать информацию «туда и обратно» без объявления громоздких глобальных переменных в модуле конфигурации, и почему предопределенная процедура ПриПовторномОткрытии() не срабатывает при простом переключении окон мышью? Разберем особенности работы платформы 1С 7.7 и рассмотрим эффективные варианты реализации двустороннего обмена данными.
Проанализируем механику работы событий в 1С:Предприятии 7.7. Распространенное заблуждение разработчиков заключается в предположении, что ПриПовторномОткрытии() является аналогом события активации окна или получения фокуса ввода (как в современных платформах или прикладном API). На самом деле это не так.
Платформа 1С 7.7 вызывает событие ПриПовторномОткрытии() исключительно программно — в момент выполнения функции ОткрытьФорму() по отношению к объекту, форма которого уже открыта на экране. Если пользователь просто переключается между открытыми окнами кликом мыши или системными клавишами, событие ПриПовторномОткрытии() не возникает ни при каких обстоятельствах. В штатной функциональности платформы событий перехвата фокуса (активации окна) нет.
Самым гибким, надежным и чистым с точки зрения архитектуры способом является передача контекста исходной обработки во вторую форму через Форма.Параметр. Получив ссылку на чужой контекст, вторая обработка может напрямую вызывать процедуры исходной формы и модифицировать ее реквизиты.
Разберем пошаговый алгоритм реализации:
Контекст в качестве параметра. Также объявляем экспортную процедуру, которая будет принимать результат:
// В модуле формы первой обработки (Обработка1)
// Процедура для приема данных из второй обработки
Процедура ПринятьДанныеИзВторой(ДанныеРезультата) Экспорт
Сообщить("Получены данные: " + ДанныеРезультата);
// Обработка полученных значений
КонецПроцедуры
// Вызов второй обработки
Процедура ОткрытьПодчиненнуюОбработку()
// Передаем собственный контекст во вторую форму
ОткрытьФорму("Обработка.Обработка2", Контекст);
КонецПроцедуры
// В модуле формы второй обработки (Обработка2)
Перем КонтекстПервой;
Процедура ПриОткрытии()
// Проверяем, передан ли контекст
Если ТипЗначения(Форма.Параметр) = 100 Тогда
КонтекстПервой = Форма.Параметр;
КонецЕсли;
КонецПроцедуры
Процедура КнопкаВернутьДанные()
// Проверяем, что первая форма все еще открыта (тип 100)
Если ТипЗначения(КонтекстПервой) = 100 Тогда
КонтекстПервой.ПринятьДанныеИзВторой("ВыбранноеЗначение");
// При необходимости обновляем форму на экране
КонтекстПервой.Форма.Обновить();
Иначе
Предупреждение("Исходная форма была закрыта!");
КонецЕсли;
КонецПроцедуры
Важный нюанс: тип значения открытой формы в 1С 7.7 равен 100 (групповой контекст). Если пользователь закроет первое окно, переменная перестанет указывать на рабочий контекст (тип изменится на 0). Проверка Если ТипЗначения(КонтекстПервой) = 100 защитит код от критических ошибок времени выполнения при обращении к закрытому окну.
Если нет необходимости напрямую дергать процедуры чужого модуля, можно использовать особенность передачи сложных объектов по ссылке. Созданный в первой обработке СписокЗначений или ТаблицаЗначений передается через параметр во вторую форму. Обе формы в этом случае работают с единой областью памяти.
Посмотрим на пример организации взаимодействия:
// Модуль первой обработки
Перем ОбщийБуфер;
Процедура ОткрытьОкноВыбора()
ОбщийБуфер = СоздатьОбъект("СписокЗначений");
ОбщийБуфер.ДобавитьЗначение("КлючевойПараметр", "Ключ");
// Передаем ссылку на объект
ОткрытьФорму("Обработка.Обработка2", ОбщийБуфер);
КонецПроцедуры
Во второй обработке мы модифицируем этот же объект:
// Модуль второй обработки
Процедура ЗаписатьРезультат()
Если ТипЗначенияСтр(Форма.Параметр) = "СписокЗначений" Тогда
Форма.Параметр.Установить("Результат", "НайденноеЗначение");
КонецЕсли;
КонецПроцедуры
Любые изменения, сделанные во второй форме, мгновенно видны в первой обработке в объекте ОбщийБуфер без необходимости синхронизации.
Если архитектурно требуется передавать результат именно через штатную процедуру ПриПовторномОткрытии(), ее можно инициировать искусственно. Для этого вторая обработка в нужный момент явно выполняет оператор ОткрытьФорму(), обращаясь к первой обработке.
Разберем, как это работает:
// Во второй обработке по кнопке или действию пользователя
Процедура ОтправитьДанныеВПервую()
ПараметрыВозврата = "РезультатРасчета";
// Повторный вызов не дублирует форму, а передает управление в ПриПовторномОткрытии
ОткрытьФорму("Обработка.Обработка1", ПараметрыВозврата);
КонецПроцедуры
В этом случае платформа активизирует существующее окно первой обработки и выполнит процедуру:
// В модуле первой обработки
Процедура ПриПовторномОткрытии(Парам)
Сообщить("Сработало повторное открытие с данными: " + Парам);
КонецПроцедуры
Обратите внимание: если при первичном вызове обработки использовался модификатор множественного открытия через решетку (к примеру, ОткрытьФорму("Обработка.Моя#1")), то и повторный вызов обязан содержать аналогичный идентификатор после #, иначе откроется новое окно вместо передачи параметров в существующее.