При разработке внешних обработок на платформе 1С:Предприятие 8.3 с использованием управляемых форм, часто возникает задача переключения между различными формами внутри одной обработки. Например, по нажатию на кнопку на одной форме требуется открыть другую форму, которая также принадлежит этой же обработке. Начинающие разработчики, ранее не работавшие с управляемыми формами, сталкиваются с трудностями при вызове метода ОткрытьФорму(), так как он возвращает ошибку, если путь к форме указан неверно. Более того, даже если форму удается открыть, возникает другая проблема: каждая новая форма открывает по сути новый экземпляр обработки, и данные, введенные в одной форме, не видны в другой. В этом материале мы подробно рассмотрим, как правильно решить эти задачи, используя функционал платформы 1С.
Основная сложность при открытии форм заключается в правильном формировании пути к форме. Когда вы открываете внешнюю обработку через меню «Файл — Открыть», 1С присваивает ей временное, динамическое имя в памяти, которое не всегда соответствует имени файла или имени объекта метаданных. Попытки использовать жестко прописанный путь, например, ОткрытьФорму("ВнешняяОбработка.ИмяВашейОбработки.Форма.ИмяФормы"), могут приводить к ошибкам.
Мы выясним, как правильно динамически сформировать имя формы, чтобы избежать ошибок. Для этого нам понадобится свойство ИмяФормы текущей формы, которое содержит полный путь к ней.
Анализируем путь к форме:
Для начала, давайте посмотрим, какой путь имеет текущая форма. В отладчике или с помощью сообщения пользователю можно узнать полное имя текущей формы, например:
Сообщить(ЭтаФорма.ИмяФормы); // В управляемой форме
Мы увидим строку примерно такого вида: ВнешняяОбработка.ВременнаяОбработка_12345.Форма.ОсновнаяФорма или ВнешняяОбработка.ЖурналЗаказов.Форма.РулоннаяМашина. Важно заметить, что между именем обработки и именем формы всегда присутствует ключевое слово Форма.. Имя обработки (например, ВременнаяОбработка_12345 или ЖурналЗаказов) может быть динамическим, особенно если обработка открыта через «Файл – Открыть».
Динамическое формирование имени для новой формы:
Самый надежный способ открыть другую форму, принадлежащую той же внешней обработке, — это модифицировать имя текущей формы. Метод СтрЗаменить() позволяет заменить часть строки. Таким образом, мы можем заменить имя текущей формы на имя формы, которую хотим открыть.
Предположим, у нас есть формы с именами Форма1 и Форма2 внутри одной внешней обработки. Чтобы открыть Форма2 из Форма1, мы используем следующий код:
&НаКлиенте
Процедура ОткрытьФорму2(Команда)
ИмяТекущейФормы = ЭтаФорма.ИмяФормы; // Получаем полное имя текущей формы (например, "ВнешняяОбработка.МояОбработка.Форма.Форма1")
ИмяФормыДляОткрытия = СтрЗаменить(ИмяТекущейФормы, "Форма1", "Форма2"); // Заменяем имя текущей формы на имя новой формы
ОткрытьФорму(ИмяФормыДляОткрытия); // Открываем новую форму
КонецПроцедуры
Этот подход гарантирует, что имя обработки в пути будет корректным, независимо от того, как она была открыта (через меню, программно с регистрацией во временном хранилище и т.д.).
Важный момент: префикс Форма.
Обратите внимание, что в полном имени формы всегда присутствует префикс Форма. перед именем самой формы. Пример: ВнешняяОбработка.ЖурналЗаказов.Форма.РулоннаяМашина. Если вы попытаетесь вызвать ОткрытьФорму("ВнешняяОбработка.ЖурналЗаказов.РулоннаяМашина"), как это было в сообщении 3, возникнет ошибка, потому что отсутствует Форма.. Использование СтрЗаменить автоматически сохраняет этот префикс, так как мы заменяем только само имя формы.
Как справедливо отмечено в сообщениях 5, 7 и 9, при каждом вызове ОткрытьФорму() для внешней обработки, система создает новый экземпляр объекта обработки в памяти. Это означает, что если вы ввели какие-то данные в реквизит одной формы, эти данные не будут автоматически доступны в реквизите другой формы, поскольку они работают с разными экземплярами объекта обработки. Чтобы обеспечить корректный обмен данными, мы можем применить несколько подходов.
Это один из самых распространенных и гибких способов передачи данных между формами, особенно когда требуется передать сложный объект или большой объем информации.
Подготовка данных в первой форме:
В первой форме, перед открытием второй, мы подготавливаем данные для передачи. Чаще всего это сам объект внешней обработки, реквизиты которого мы хотим использовать в другой форме. Объект обработки (экземпляр типа ОбъектВнешнейОбработки) находится на сервере. Мы должны получить его, поместить во временное хранилище и передать его адрес.
&НаСервереБезКонтекста
Функция ПолучитьОбъектОбработкиИАдресВоВременномХранилище(УникальныйИдентификаторФормы)
// РеквизитФормыВЗначение("Объект") возвращает копию объекта обработки на сервере.
// Он нужен, чтобы получить доступ к реквизитам модуля объекта обработки.
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
// Помещаем объект во временное хранилище.
// УникальныйИдентификаторФормы используется для того, чтобы хранилище было связано с конкретной формой
// и автоматически очищалось при ее закрытии.
Адрес = ПоместитьВоВременноеХранилище(ОбъектОбработки, УникальныйИдентификаторФормы);
Возврат Адрес;
КонецФункции
&НаКлиенте
Процедура ОткрытьВторуюФормуСДанными(Команда)
// Получаем адрес объекта обработки во временном хранилище
АдресОбъекта = ПолучитьОбъектОбработкиИАдресВоВременномХранилище(ЭтаФорма.УникальныйИдентификатор);
// Формируем параметры для открытия второй формы
ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("АдресОбъектаОбработки", АдресОбъекта);
// Можно передать и другие параметры
ПараметрыОткрытия.Вставить("ЗаголовокФормы", "Вторая форма с переданными данными");
// Формируем имя второй формы
ИмяТекущейФормы = ЭтаФорма.ИмяФормы;
ИмяФормыДляОткрытия = СтрЗаменить(ИмяТекущейФормы, "Форма1", "Форма2");
// Открываем вторую форму, передавая параметры
ОткрытьФорму(ИмяФормыДляОткрытия, ПараметрыОткрытия);
КонецПроцедуры
Получение данных во второй форме:
Во второй форме в обработчике события ПриСозданииНаСервере мы извлекаем переданный адрес из параметров открытия и получаем объект обработки из временного хранилища. Затем мы присваиваем этот объект реквизиту формы Объект.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("АдресОбъектаОбработки") Тогда
АдресОбъекта = Параметры.АдресОбъектаОбработки;
// Получаем объект из временного хранилища
ОбъектОбработки = ПолучитьИзВременногоХранилища(АдресОбъекта);
Если ОбъектОбработки <> Неопределено Тогда
// Присваиваем полученный объект реквизиту формы "Объект"
// Это позволяет второй форме работать с тем же экземпляром данных.
ЗначениеВРеквизитФормы(ОбъектОбработки, "Объект");
КонецЕсли;
КонецЕсли;
// Если передавался заголовок, можно его установить
Если Параметры.Свойство("ЗаголовокФормы") Тогда
ЭтаФорма.Заголовок = Параметры.ЗаголовокФормы;
КонецЕсли;
КонецПроцедуры
Теперь обе формы работают с одним и тем же объектом обработки, и изменения, сделанные в реквизитах одной формы (после сохранения на сервер), будут доступны в другой.
Если формы имеют иерархическую связь (одна является "владельцем" другой), мы можем использовать параметр Владелец при открытии формы. Это позволяет дочерней форме обращаться к данным родительской формы напрямую.
Открытие подчиненной формы с указанием владельца:
В первой форме, при открытии второй, мы указываем текущую форму как владельца:
&НаКлиенте
Процедура ОткрытьПодчиненнуюФорму(Команда)
// Формируем параметры для открытия второй формы
ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("Владелец", ЭтаФорма); // Указываем текущую форму как владельца
// Формируем имя второй формы
ИмяТекущейФормы = ЭтаФорма.ИмяФормы;
ИмяФормыДляОткрытия = СтрЗаменить(ИмяТекущейФормы, "Форма1", "Форма2");
// Открываем вторую форму
ОткрытьФорму(ИмяФормыДляОткрытия, ПараметрыОткрытия);
КонецПроцедуры
Доступ к данным владельца во второй форме:
Во второй форме, мы можем получить доступ к владельцу через свойство ВладелецФормы. Через него можно вызывать экспортные методы и получать значения реквизитов формы владельца.
&НаКлиенте
Процедура ПрочитатьДанныеИзВладельца(Команда)
Если ЭтаФорма.ВладелецФормы <> Неопределено Тогда
// Проверяем, является ли владелец формой нужного типа
Если ТипЗнч(ЭтаФорма.ВладелецФормы) = Тип("ФормаУправляемаяВнешнейОбработки") Тогда
// Например, получаем значение реквизита "МойРеквизит" из формы-владельца
ЗначениеРеквизитаВладельца = ЭтаФорма.ВладелецФормы.МойРеквизит;
Сообщить("Значение из формы-владельца: " + ЗначениеРеквизитаВладельца);
// Или вызываем экспортную процедуру владельца
ЭтаФорма.ВладелецФормы.ЭкспортнаяПроцедураВладельца();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот способ удобен для тесно связанных форм, когда одна форма явно подчинена другой.
Механизм оповещений (Оповестить(), ОбработкаОповещения(), ОписаниеОповещения()) является стандартным и наиболее универсальным способом взаимодействия между формами в управляемом приложении 1С, особенно если формы не имеют прямой иерархической связи или требуется асинхронное взаимодействие.
Отправка оповещения из одной формы:
Первая форма отправляет оповещение с определенным именем события и необязательными параметрами. Все открытые формы, которые подписаны на это событие, получат оповещение.
&НаКлиенте
Процедура ОтправитьОповещение(Команда)
ПараметрыОповещения = Новый Структура;
ПараметрыОповещения.Вставить("Сообщение", "Данные обновлены!");
ПараметрыОповещения.Вставить("НовоеЗначение", "Значение_X");
// Оповещаем другие формы о событии "ОбновлениеДанных"
// ЭтаФорма указывает отправителя, если получатель хочет отфильтровать оповещения
Оповестить("ОбновлениеДанных", ПараметрыОповещения, ЭтаФорма);
КонецПроцедуры
Обработка оповещения в другой форме:
Во второй форме (или любой другой), которая должна реагировать на это событие, необходимо создать экспортную процедуру с именем ОбработкаОповещения.
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник) Экспорт
Если ИмяСобытия = "ОбновлениеДанных" Тогда
Сообщить("Получено оповещение об обновлении данных.");
Если ТипЗнч(Параметр) = Тип("Структура") Тогда
Сообщить("Сообщение: " + Параметр.Сообщение);
Сообщить("Новое значение: " + Параметр.НовоеЗначение);
// Обновляем реквизит формы на основе полученных данных
ЭтаФорма.МойРеквизитФормы = Параметр.НовоеЗначение;
КонецЕсли;
// Можно проверить Источник, если нужно знать, какая форма отправила оповещение
// Если Источник = ЭтаФорма.ВладелецФормы Тогда
// // Действия, если оповещение пришло от владельца
// КонецЕсли;
КонецЕсли;
КонецПроцедуры
Возврат результата с помощью ОписаниеОповещения:
Если вам нужно, чтобы открытая форма вернула результат обратно в вызывающую форму, используйте метод ОткрытьФорму() с параметром ОповещениеОЗакрытии, который принимает объект ОписаниеОповещения.
// В первой форме, при открытии второй:
&НаКлиенте
Процедура ОткрытьФормуДляВыбора(Команда)
// ОписаниеОповещения: процедура, которая будет вызвана при закрытии открываемой формы,
// и параметры, которые будут переданы в эту процедуру.
Оповещение = Новый ОписаниеОповещения("ОбработатьВыборВФорме", ЭтаФорма);
ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("ТекстДляВыбора", "Выберите значение");
ИмяТекущейФормы = ЭтаФорма.ИмяФормы;
ИмяФормыДляОткрытия = СтрЗаменить(ИмяТекущейФормы, "Форма1", "Форма2");
ОткрытьФорму(ИмяФормыДляОткрытия, ПараметрыОткрытия, ЭтаФорма, , , , Оповещение);
КонецПроцедуры
// В первой форме, обработчик оповещения о закрытии:
&НаКлиенте
Процедура ОбработатьВыборВФорме(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
Если РезультатЗакрытия <> Неопределено Тогда
Сообщить("Выбранное значение: " + РезультатЗакрытия);
ЭтаФорма.ВыбранноеЗначение = РезультатЗакрытия;
КонецЕсли;
КонецПроцедуры
// Во второй форме, при закрытии (или после выбора):
&НаКлиенте
Процедура ПриЗакрытии(Отказ, СтандартнаяОбработка)
// Передаем результат обратно в форму-владелец
// Например, выбранное значение из реквизита формы
ОповеститьОВыборе(ЭтаФорма.ВыбранноеЗначение);
КонецПроцедуры
Механизм оповещений является мощным инструментом для организации взаимодействия между формами, позволяя им обмениваться информацией без жесткой зависимости друг от друга.
В сообщении 17 упоминается возможность сохранения указателя на открытую форму и вызова ее экспортных процедур. Этот метод позволяет организовать клиент-клиент взаимодействие без серверных вызовов, что может быть полезно для быстрой реакции на действия пользователя.
Сохранение указателя на открытую форму:
При открытии формы, метод ОткрытьФорму() возвращает ссылку на открытую форму, которую можно сохранить в клиентской переменной.
&НаКлиенте
Перем СсылкаНаВторуюФорму; // Переменная для хранения ссылки
Процедура ОткрытьИСохранитьСсылку(Команда)
ИмяТекущейФормы = ЭтаФорма.ИмяФормы;
ИмяФормыДляОткрытия = СтрЗаменить(ИмяТекущейФормы, "Форма1", "Форма2");
// Открываем форму и сохраняем ссылку
СсылкаНаВторуюФорму = ОткрытьФорму(ИмяФормыДляОткрытия);
КонецПроцедуры
Вызов экспортных процедур второй формы:
Во второй форме необходимо определить экспортные процедуры, которые мы хотим вызывать из первой формы.
// В модуле формы "Форма2"
&НаКлиенте
Процедура ОбновитьДанныеНаФорме2(НовыеДанные) Экспорт
ЭтаФорма.РеквизитФормы2 = НовыеДанные;
Сообщить("Данные в Форме2 обновлены: " + НовыеДанные);
КонецПроцедуры
Теперь, из первой формы, мы можем вызвать эту процедуру через сохраненную ссылку:
&НаКлиенте
Процедура ОбновитьВторуюФормуЧерезСсылку(Команда)
Если СсылкаНаВторуюФорму <> Неопределено Тогда
// Вызываем экспортную процедуру второй формы
СсылкаНаВторуюФорму.ОбновитьДанныеНаФорме2("Обновлено из Формы1");
Иначе
Сообщить("Вторая форма не открыта или ссылка потеряна.");
КонецЕсли;
КонецПроцедуры
Этот метод предоставляет прямой и быстрый способ взаимодействия, но требует тщательного контроля за жизненным циклом ссылок (например, что произойдет, если вторая форма будет закрыта, а ссылка останется).
В управляемых формах часто возникает путаница между ЭтотОбъект и ЭтаФорма, а также с тем, как обращаться к данным самой обработки. Давайте разберем эти понятия.
ЭтотОбъект vs. ЭтаФорма:
ЭтаФорма (устаревший синоним) / ЭтотОбъект (актуальный синоним) в модуле управляемой формы всегда ссылается на экземпляр самой управляемой формы. Этот объект имеет тип УправляемаяФорма (или специализированный тип, например, ФормаУправляемаяВнешнейОбработки) и предоставляет доступ к элементам формы, реквизитам формы, заголовку, параметрам открытия и методам формы.
// Пример использования ЭтотОбъект (для формы)
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Сообщить("Имя формы: " + ЭтотОбъект.ИмяФормы);
ЭтотОбъект.Заголовок = "Моя Форма";
КонецПроцедуры
Чтобы обратиться к объекту данных, который является источником данных для формы (то есть к экземпляру самой внешней обработки, где описаны ее реквизиты и логика), используется специальный реквизит формы с именем Объект. Этот реквизит по умолчанию связывается с объектом метаданных, лежащим в основе формы (в нашем случае — с внешней обработкой).
Взаимодействие с объектом обработки на сервере:
Чтобы работать с реквизитами или методами самой внешней обработки (не формы!), которые находятся в модуле объекта обработки, мы используем связку методов:
РеквизитФормыВЗначение("Объект"): Этот метод, вызываемый на сервере, создает копию объекта обработки из данных формы и возвращает ее. Все операции с этим возвращенным объектом происходят на сервере.
ЗначениеВРеквизитФормы(ОбъектОбработки, "Объект"): После того как вы внесли изменения в полученный на сервере объект обработки, этот метод позволяет вернуть эти изменения обратно в реквизит формы, чтобы они стали доступны для отображения и дальнейшего взаимодействия на клиенте.
&НаСервере
Процедура ВыполнитьДействиеСервер(НовоеЗначениеРеквизитаОбъекта)
// Получаем копию объекта обработки с сервера
МойОбъектОбработки = РеквизитФормыВЗначение("Объект");
// Изменяем реквизит объекта обработки
МойОбъектОбработки.МойРеквизитОбработки = НовоеЗначениеРеквизитаОбъекта;
// Можно вызвать метод модуля объекта обработки
МойОбъектОбработки.ВыполнитьЛогику();
// Возвращаем измененный объект обратно в реквизит формы
ЗначениеВРеквизитФормы(МойОбъектОбработки, "Объект");
КонецПроцедуры
&НаКлиенте
Процедура КнопкаВыполнитьНаСервере(Команда)
ВыполнитьДействиеСервер("НовоеЗначение"); // Вызов серверной процедуры
КонецПроцедуры
Важно помнить, что каждое обращение к РеквизитФормыВЗначение("Объект") создает новую копию. Если две формы должны работать с одним и тем же экземпляром объекта обработки, необходимо использовать методы передачи данных, описанные выше (например, через временное хранилище), чтобы они получали одну и ту же "копию-оригинал" объекта.
Как предлагает сообщение 18, во многих случаях, когда нет острой необходимости в отдельных окнах, проще и эффективнее использовать одну форму, разделив ее логически на несколько "страниц" или "вкладок" с помощью элементов формы типа ГруппаВидаСтраницы или ГруппаВидаЗакладки.
Общие реквизиты: Все реквизиты формы являются общими для всех страниц, нет необходимости в сложных механизмах передачи данных между "формами".
Производительность: Не нужно открывать и инициализировать новые формы, что ускоряет работу приложения.
Удобство разработки: Упрощается отладка и поддержка, так как вся логика сосредоточена в одном модуле формы.
Единый контекст: Всегда работаете с одним и тем же экземпляром объекта обработки.
Создание элемента "Страницы":
В конфигураторе, на вкладке "Элементы" формы, создайте новый элемент типа Группа. Для этой группы установите свойство Вид в Страницы или Закладки.
Добавление страниц:
Внутри этой группы "Страницы" создайте несколько других групп. Для каждой из этих внутренних групп установите свойство Вид в Страница. Каждая такая группа будет отдельной страницей, на которую вы можете помещать свои элементы управления.
Переключение между страницами:
Переключение между страницами осуществляется программно, путем установки свойства ТекущаяСтраница для группы "Страницы".
&НаКлиенте
Процедура ПереключитьсяНаСтраницу2(Команда)
// "ГруппаСтраницы" - имя вашей группы, которая имеет Вид = Страницы
// "Страница2" - имя одной из внутренних групп с Видом = Страница
ЭтаФорма.Элементы.ГруппаСтраницы.ТекущаяСтраница = ЭтаФорма.Элементы.Страница2;
КонецПроцедуры
&НаКлиенте
Процедура ПереключитьсяНаСтраницу1(Команда)
ЭтаФорма.Элементы.ГруппаСтраницы.ТекущаяСтраница = ЭтаФорма.Элементы.Страница1;
КонецПроцедуры
Этот подход часто является предпочтительным для организации многоэтапных или многофункциональных интерфейсов внутри одной внешней обработки, когда нет жестких требований к открытию именно отдельных окон.
Критически важно учитывать, что если внешняя обработка открывается в информационной базе, где включен профиль безопасности (что часто бывает в облачных сервисах или крупных корпоративных внедрениях), прямые вызовы некоторых методов или работа с файлами на диске могут быть заблокированы. В таких случаях внешние обработки должны быть предварительно зарегистрированы в справочнике «Дополнительные отчеты и обработки» с присвоением им прав, соответствующих требованиям безопасного режима. Без регистрации некоторые функции, включая программное открытие форм по пути к файлу или работу с временным хранилищем, могут не функционировать или вызывать ошибки безопасности.
Мы рассмотрели различные подходы к организации работы с несколькими формами в рамках одной внешней обработки 1С:Предприятия 8.3 на управляемых формах. Выбор конкретного метода зависит от требований к интерфейсу, сложности обмена данными и производительности. Понимание этих механизмов позволяет создавать гибкие и эффективные решения.