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