Как организовать совместное использование данных объекта между несколькими формами одной обработки?

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

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

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

Почему нельзя просто использовать один и тот же «Объект»?

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

Решение 1: Использование механизма владельца формы

Это самый простой и часто используемый способ, когда одна форма является «дочерней» по отношению к другой. Если мы открываем «Дополнительную» форму из «Основной», мы можем назначить основную форму владельцем. Это даст нам возможность напрямую обращаться к контексту основной формы.

Рассмотрим пошагово, как это реализовать. В модуле «Основной» формы при открытии «Дополнительной» пропишем следующий код:


&НаКлиенте
Процедура ОткрытьДополнительнуюФорму(Команда)
    ПараметрыФормы = Новый Структура;
    // Можно передать какие-то начальные данные, если нужно
    ОткрытьФорму("Обработка.МояОбработка.Форма.ДополнительнаяФорма", ПараметрыФормы, ЭтаФорма);
КонецПроцедуры

Теперь в модуле «Дополнительной» формы мы можем обращаться к данным владельца. Например, чтобы получить значение реквизита или вызвать экспортный метод:


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

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

Решение 2: Синхронизация через Временное хранилище

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

Разберем алгоритм действий:

  1. В основной форме преобразуем Объект (данные формы) в реальный прикладной объект.
  2. Помещаем этот объект во временное хранилище.
  3. Передаем адрес хранилища в параметрах открываемой формы.
  4. В новой форме при создании извлекаем объект и загружаем его данные в свой реквизит Объект.

Пример кода для основной формы:


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

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

Пример кода для дополнительной формы (в обработчике ПриСозданииНаСервере):


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

Решение 3: Использование механизма оповещений (Оповестить / ОбработкаОповещения)

Предыдущие методы хороши для передачи данных в момент открытия. Но что делать, если пользователь работает в двух формах параллельно и хочет, чтобы изменения в «Дополнительной» форме тут же отображались в «Основной»? Для этого проанализируем механизм Оповестить.

В дополнительной форме при изменении данных вызываем оповещение:


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

В основной форме создаем обработчик события ОбработкаОповещения:


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

Вынос логики в модуль объекта

Выясним причину, по которой автор темы стремится к «единому объекту». Часто это желание избежать дублирования кода. Если у вас есть сложная проверка данных или алгоритм расчета, не пишите его в модуле формы. Проанализируем правильную архитектуру:

  1. Вся бизнес-логика должна находиться в модуле объекта обработки.
  2. В формах вызываются лишь процедуры-обертки.

Например, в модуле объекта создаем экспортную процедуру:


Процедура РассчитатьСкидки() Экспорт
    // Сложный код расчета, использующий реквизиты объекта
    Сумма = Сумма * (1 - Скидка/100);
КонецПроцедуры

Тогда в любой из форм (основной или дополнительной) вызов будет выглядеть одинаково:


&НаСервере
Процедура ВыполнитьРасчетНаСервере()
    ТекОбъект = РеквизитФормыВЗначение("Объект");
    ТекОбъект.РассчитатьСкидки();
    ЗначениеВРеквизитФормы(ТекОбъект, "Объект");
КонецПроцедуры

Резюме и рекомендации

Мы рассмотрели различные способы «эмуляции» работы с единым объектом в разных формах. Подведем итоги:

1. Если формы открываются последовательно: Используйте передачу данных через ВременноеХранилище в параметрах формы. Это обеспечит полную передачу состояния объекта.

2. Если формы работают одновременно: Используйте механизм Владелец для простого доступа и Оповестить() для синхронизации изменений в реальном времени.

3. Если нужно избежать дублирования кода: Максимально выносите алгоритмы в модуль объекта. Это позволит каждой форме работать со своим экземпляром объекта, но по единым правилам.

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

← На главную