Как открыть вторую форму внешней обработки в управляемом приложении с доступом к данным и табличной части первой формы?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе с обычных форм (толстого клиента) на управляемые формы в платформе 1С:Предприятие 8 разработчики часто сталкиваются с неожиданным поведением системы. Один из классических сценариев: в основной форме внешней обработки пользователь заполняет табличную часть, затем открывает вспомогательную форму (например, форму настроек), но эта вторая форма оказывается абсолютно пустой. Она «не видит» ни введенных реквизитов, ни строк табличной части.

В этой статье мы подробно разберем, почему так происходит с точки зрения архитектуры управляемого приложения, и рассмотрим надежные технические решения этой проблемы, включая работу с Временным хранилищем и свойством ВладелецФормы.

Выясняем причину: особенности жизненного цикла объектов в УФ

Давайте проанализируем ситуацию. В обычном приложении (в толстом клиенте) данные объекта обработки существовали в едином контексте. Разные формы одной и той же обработки могли легко обращаться к одному и тому же экземпляру ОбработкаОбъект в памяти компьютера.

В управляемом приложении (УФ) архитектура принципиально иная. Здесь действует строгая изоляция контекстов:

  1. Разделение клиента и сервера: Данные формы на клиенте представлены специальными подготовленными типами (например, ДанныеФормыСтруктура или ДанныеФормыКоллекция для табличных частей).
  2. Изоляция экземпляров: Когда мы вызываем метод ОткрытьФорму(), платформа создает на сервере абсолютно новый, чистый экземпляр объекта обработки.
  3. Отсутствие ссылки в БД: Если для документов или справочников платформа умеет автоматически сопоставлять данные по уникальной ссылке (Ссылка), то у внешних обработок ссылки в базе данных нет. Платформа просто не знает, что новая форма должна быть связана с данными старой формы, и инициализирует объект типа ОбработкаОбъект с нуля.

Таким образом, две разные формы одной внешней обработки — это два абсолютно независимых контекста и два разных объекта в памяти сервера. Данные из первой формы автоматически во вторую форму не попадут.

Типичная ошибка при открытии формы внешней обработки

Прежде чем переходить к передаче данных, обратим внимание на важный нюанс. Часто разработчики пытаются открыть вторую форму так:

ОткрытьФорму("Обработка.МояОбработка.Форма.ФормаНастройки");

Если ваша обработка является внешней (запущена из файла .epf), такой код вызовет ошибку, так как класс Обработка.Имя ищется внутри метаданных конфигурации. Для внешних обработок путь должен начинаться с префикса ВнешняяОбработка.

Поскольку при открытии внешнего файла платформа присваивает обработке динамическое имя (например, ВнешняяОбработка.ИмяВременногоФайла_...), прописывать имя жестким кодом нельзя. Давайте разберем, как получить имя второй формы динамически, используя имя текущей формы:


// Находим положение системного разделителя ".Форма."
ПозицияРазделителя = СтрНайти(ЭтаФорма.ИмяФормы, ".Форма.", НаправлениеПоиска.Конец);

// Формируем путь ко второй форме, подставив её имя (например, "ФормаНастройки")
ИмяВторойФормы = Лев(ЭтаФорма.ИмяФормы, ПозицияРазделителя + 6) + "ФормаНастройки";

Этот прием гарантирует, что система откроет вспомогательную форму именно из этого конкретного файла внешней обработки.

Решение №1: Передача данных через Временное хранилище (Рекомендуемый способ)

Попытка передать всю табличную часть напрямую через параметры формы завершится ошибкой, так как сложные типы данных (такие как таблицы значений) недопустимо передавать в параметрах напрямую. Лучший и наиболее производительный способ обмена данными между формами — использование Временного хранилища.

Рассмотрим по шагам, как реализовать этот механизм.

Шаг 1. Код в Основной форме (откуда передаем данные)

Перед открытием второй формы мы выгружаем табличную часть и реквизиты в структуру, сериализуем её во временное хранилище на сервере, получаем адрес и передаем этот адрес в параметрах открытия формы.


&НаКлиенте
Процедура ОткрытьВторуюФорму(Команда)
    
    // Помещаем данные ТЧ во временное хранилище на сервере
    АдресДанныхВХ = ПоместитьДанныеВоВременноеХранилищеНаСервере();
    
    // Готовим параметры для открытия подчиненной формы
    ПараметрыВторойФормы = Новый Структура;
    ПараметрыВторойФормы.Вставить("АдресДанных", АдресДанныхВХ);
    
    // Динамически вычисляем путь к форме настройки
    ПозицияРазделителя = СтрНайти(ЭтаФорма.ИмяФормы, ".Форма.", НаправлениеПоиска.Конец);
    ИмяВторойФормы = Лев(ЭтаФорма.ИмяФормы, ПозицияРазделителя + 6) + "ФормаНастройки";
    
    // Открываем вторую форму, передав параметры и назначив текущую форму владельцем
    ОткрытьФорму(ИмяВторойФормы, ПараметрыВторойФормы, ЭтаФорма);
    
КонецПроцедуры

&НаСервере
Функция ПоместитьДанныеВоВременноеХранилищеНаСервере()
    
    // Создаем структуру для упаковки передаваемых данных
    ПакетДанных = Новый Структура;
    ПакетДанных.Вставить("ТабличнаяЧастьТовары", Объект.Товары.Выгрузить());
    ПакетДанных.Вставить("СкладВыбора", Объект.Склад);
    
    // Привязываем адрес хранилища к Уникальному Идентификатору текущей формы,
    // чтобы данные автоматически очистились при закрытии формы
    Возврат ПоместитьВоВременноеХранилище(ПакетДанных, ЭтаФорма.УникальныйИдентификатор);
    
КонецФункции

Шаг 2. Код во Второй форме (которая принимает данные)

В модуле открываемой формы (например, в «ФормаНастройки») обрабатываем полученные параметры при создании формы на сервере.


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Проверяем, передан ли адрес временного хранилища
    Если Параметры.Свойство("АдресДанных") Тогда
        
        // Извлекаем структуру из Временного хранилища
        ДанныеДляЗаполнения = ПолучитьИзВременногоХранилища(Параметры.АдресДанных);
        
        Если ДанныеДляЗаполнения <> Неопределено Тогда
            // Заполняем реквизиты и табличную часть на новой форме
            Объект.Товары.Загрузить(ДанныеДляЗаполнения.ТабличнаяЧастьТовары);
            Объект.Склад = ДанныеДляЗаполнения.СкладВыбора;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Как вернуть измененные данные обратно в основную форму?

Зачастую во второй форме пользователь редактирует параметры или наполняет таблицу, и эти изменения требуется вернуть назад. Использование модальных окон в современном интерфейсе 1С запрещено, поэтому мы применим асинхронный механизм оповещений (ОписаниеОповещенияОЗакрытии).

Проанализируем этот процесс:

1. В основной форме при вызове открытия мы указываем процедуру, которая будет ожидать возврата данных:


&НаКлиенте
Процедура ОткрытьВторуюФормуСВозвратом(Команда)
    
    АдресДанныхВХ = ПоместитьДанныеВоВременноеХранилищеНаСервере();
    
    ПараметрыВторойФормы = Новый Структура;
    ПараметрыВторойФормы.Вставить("АдресДанных", АдресДанныхВХ);
    
    ПозицияРазделителя = СтрНайти(ЭтаФорма.ИмяФормы, ".Форма.", НаправлениеПоиска.Конец);
    ИмяВторойФормы = Лев(ЭтаФорма.ИмяФормы, ПозицияРазделителя + 6) + "ФормаНастройки";
    
    // Создаем описание оповещения, указывая имя принимающей процедуры
    ОповещениеОЗакрытии = Новый ОписаниеОповещения("ПослеЗакрытияФормыНастройки", ЭтаФорма);
    
    // Передаем оповещение седьмым параметром метода ОткрытьФорму
    ОткрытьФорму(ИмяВторойФормы, ПараметрыВторойФормы, ЭтаФорма, , , , ОповещениеОЗакрытии);
    
КонецПроцедуры

2. Во второй форме при нажатии кнопки «ОК» или «Применить» мы сохраняем измененные данные обратно в хранилище и закрываем форму, передавая адрес обратно:


&НаКлиенте
Процедура ЗаписатьИЗакрыть(Команда)
    
    // Помещаем измененные данные в ВХ на сервере
    АдресИзмененныхДанных = ПоместитьИзмененияВХНаСервере();
    
    // Закрываем форму и возвращаем адрес обратно
    Закрыть(АдресИзмененныхДанных);
    
КонецПроцедуры

&НаСервере
Функция ПоместитьИзмененияВХНаСервере()
    
    ПакетОбратныхДанных = Новый Структура;
    ПакетОбратныхДанных.Вставить("ТабличнаяЧастьТовары", Объект.Товары.Выгрузить());
    
    Возврат ПоместитьВоВременноеХранилище(ПакетОбратныхДанных, ЭтаФорма.УникальныйИдентификатор);
    
КонецФункции

3. В основной форме обрабатываем возвращенные данные в процедуре-приемнике:


&НаКлиенте
Процедура ПослеЗакрытияФормыНастройки(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    
    // Если результат пустой — пользователь просто закрыл форму крестиком
    Если РезультатЗакрытия <> Неопределено Тогда
        ОбновитьДанныеОсновнойФормыНаСервере(РезультатЗакрытия);
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Процедура ОбновитьДанныеОсновнойФормыНаСервере(АдресРезультата)
    
    ИзмененныеДанные = ПолучитьИзВременногоХранилища(АдресРезультата);
    Если ИзмененныеДанные <> Неопределено Тогда
        Объект.Товары.Загрузить(ИзмененныеДанные.ТабличнаяЧастьТовары);
    КонецЕсли;
    
КонецПроцедуры

Решение №2: Использование свойства «ВладелецФормы»

Существует альтернативный вариант интеграции. При вызове метода ОткрытьФорму() третьим параметром мы можем явно назначить текущую форму владельцем открываемой формы:

ОткрытьФорму(ИмяВторойФормы, ПараметрыФормы, ЭтаФорма);

В этом случае во второй форме на клиенте становится доступно свойство ЭтаФорма.ВладелецФормы. Через него можно напрямую обращаться к реквизитам первой формы:


&НаКлиенте
Процедура ИзменитьЗначениеНаВладельце(Команда)
    // Напрямую меняем простой реквизит на форме-владельце
    ЭтаФорма.ВладелецФормы.Объект.Склад = "Основной склад";
КонецПроцедуры

Недостатки прямого изменения через «ВладелецФормы»:

Поэтому метод с Временным хранилищем считается в стандартах разработки 1С наиболее чистым, быстрым и безопасным подходом.

← На главную