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