В процессе разработки внешних обработок на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с задачей взаимодействия между несколькими формами. Типичный сценарий: во вспомогательной форме (например, форме подбора или настроек) формируется ТаблицаЗначений, данные из которой должны попасть в ТабличнуюЧасть основной формы обработки. Рассмотрим подробно, как правильно реализовать этот механизм, избегая типичных ошибок производительности и архитектурных просчетов.
Основная сложность заключается в том, что в управляемом приложении каждая форма внешней обработки — это отдельный контекст. Когда мы открываем вторую форму, платформа создает новый экземпляр объекта. Изменения, внесенные в Объект внутри вспомогательной формы, не отражаются автоматически на Объекте основной формы. Нам необходимо явно передать данные из контекста одной формы в другую.
Разберем по шагам наиболее эффективные способы решения этой задачи.
Это «золотой стандарт» разработки в среде 1С. Суть метода заключается в помещении данных во временное хранилище во вспомогательной форме и их извлечении в основной форме после закрытия вспомогательной. Рассмотрим реализацию этого механизма.
В основной форме:
Для начала нам нужно описать процедуру открытия вспомогательной формы. Мы будем использовать ОписаниеОповещения, чтобы основная форма «знала», какую процедуру вызвать, когда вторая форма закроется.
&НаКлиенте
Процедура ОткрытьФормуПодбора(Команда)
// Описываем, куда вернуться после закрытия
Оповещение = Новый ОписаниеОповещения("ПриЗакрытииВспомогательнойФормы", ЭтаФорма);
// Открываем форму модально (блокируя владельца)
ПараметрыФормы = Новый Структура;
ОткрытьФорму("ВнешняяОбработка.ИмяВашейОбработки.Форма.ВспомогательнаяФорма",
ПараметрыФормы,
ЭтаФорма,
,,,,
Оповещение,
РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура ПриЗакрытииВспомогательнойФормы(Результат, ДопПараметры) Экспорт
// Проверяем, вернул ли нам сервер адрес во временном хранилище
Если ЭтоАдресВременногоХранилища(Результат) Тогда
ЗагрузитьДанныеИзХранилищаНаСервере(Результат);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьДанныеИзХранилищаНаСервере(Адрес)
// Извлекаем таблицу из хранилища
ТЗ = ПолучитьИзВременногоХранилища(Адрес);
// Загружаем данные в табличную часть объекта
// Используем метод Загрузить для высокой производительности
Объект.ТаблицаОтбораПоПравилам.Загрузить(ТЗ);
КонецПроцедуры
Во вспомогательной форме:
Здесь мы подготавливаем данные и закрываем форму, передавая адрес хранилища в качестве параметра метода Закрыть().
&НаКлиенте
Процедура ЗавершитьПодбор(Команда)
АдресХранилища = ПоместитьДанныеВХранилищеНаСервере();
Закрыть(АдресХранилища);
КонецПроцедуры
&НаСервере
Функция ПоместитьДанныеВХранилищеНаСервере()
// Предположим, ТЗ_Результат получена через СКД или Запрос
// Мы копируем только нужные колонки, чтобы не перегружать память
НужныеКолонки = "Номенклатура, Количество, Цена";
ТЗ_ДляПередачи = ТЗ_Результат.Скопировать(, НужныеКолонки);
// Помещаем во временное хранилище, привязывая к уникальному идентификатору формы
Возврат ПоместитьВоВременноеХранилище(ТЗ_ДляПередачи, ЭтаФорма.УникальныйИдентификатор);
КонецФункции
Рассмотрим ситуацию, когда ваша исходная таблица значений (например, полученная из ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений) содержит множество технических колонок, которые не нужны в табличной части. Проанализируем, почему использование запроса для фильтрации колонок (как предлагал автор в начале темы) является избыточным.
Вместо того чтобы помещать ТЗ в запрос и выбирать поля оттуда, гораздо быстрее использовать встроенный метод Скопировать(). Проанализируем пример:
// Вместо создания запроса к ТЗ:
// ТЗ_ТолькоНоменклатура = ТЗ_Исходная.Скопировать(, "Номенклатура");
// Объект.ТаблицаОтбора.Загрузить(ТЗ_ТолькоНоменклатура);
Важный нюанс: метод Загрузить() работает эффективно только в том случае, если имена колонок в ТаблицеЗначений полностью совпадают с именами реквизитов в ТабличнойЧасти. Если имена различаются, вам придется использовать цикл Для Каждого, что на больших объемах данных может работать медленнее.
Проанализируем распространенную ошибку, упомянутую в обсуждении: использование конструкции Строка.Номенклатура.Ссылка. Выясним, почему это считается плохой практикой.
.Ссылка вынуждает платформу выполнять дополнительное (хоть и кэшируемое) обращение к объекту. Правильно писать просто: СтрокаТЧ.Номенклатура = СтрокаТЗ.Номенклатура;.Строка.Номенклатура.Артикул) внутри циклов в серверном коде. Каждая такая точка — это потенциальный неявный запрос к БД.ЭтаФорма.ОбновитьОтображениеДанных();. Это заставит визуальные компоненты перечитать данные из реквизитов формы.Если вам требуется обновлять данные в основной форме «на лету», не закрывая вспомогательное окно, воспользуйтесь методом Оповестить().
В дополнительной форме вызываем:
Оповестить("ОбновитьТаблицу", АдресХранилища, ЭтаФорма);
В основной форме создаем обработчик события ОбработкаОповещения:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ОбновитьТаблицу" Тогда
ЗагрузитьДанныеИзХранилищаНаСервере(Параметр);
КонецЕсли;
КонецПроцедуры
Этот метод делает интерфейс более динамичным и отзывчивым для пользователя.
Мы рассмотрели основные механизмы взаимодействия между формами во внешних обработках. Главное правило — помнить о разделении контекстов и использовать Временное хранилище для передачи объемных коллекций данных (таких как ТаблицаЗначений). Использование метода Загрузить() вместо построчного перебора в цикле значительно повышает производительность вашего кода, особенно при работе с большими справочниками номенклатуры.