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