Трюки 1С 8 – Управление обычными формами из общих модулей
Продолжим цикл статей, начатый статьей Трюки 1С 8 Передача реквизитов формы документа в форму выбора.
Если с событиями объектов в 1С все хорошо – на них можно подписаться и встраивать свою логику в штатные алгоритмы, не изменяя модулей конфигурации, то, как известно, с событиями обычных форм дела обстоят хуже.
В этой статье я расскажу, как все-таки научиться управлять неуправляемыми формами из общих модулей.
1. Осмотримся
Попробуем понять, что именно может помешать нам в использовании механизма, подобного механизму подписок. В платформе есть возможность менять процедуру обработчика события формы или элемента управления на форме - можно, например, написать в общем модуле:
ФормаИлиЭлементФормы.УстановитьДействие(ИмяСобытия, Новый Действие(«ИмяВашейпроцедуры»));
Попробуйте! И столкнетесь сразу с тремя проблемами:
1 – действие можно установить только для существующего экземпляра формы объекта, т.е. нужно отследить момент создания интересующей нас формы, получить ее экземпляр и произвести подмену действия (в управляемых формах ситуация улучшилась).
2 – наша процедура, которой мы замещаем штатный обработчик события формы, согласно встроенной справке 1С должна находиться в модуле формы и сигнатура этой процедуры должна совпадать с сигнатурой штатного обработчика, иначе платформа выдаст ошибку:
УстановитьДействие(, )
…
Примечание:
Процедура должна быть экспортируемой и располагаться в модуле формы, при этом количество параметров должно совпадать с необходимым количеством параметров события, инициирующего вызов действия.
То есть необходимо добавить в модуль формы для каждого возможного числа параметров по универсальной процедуре, подменяющей штатное действие.
Кстати, условие экспортируемости, по-моему, необязательно, поскольку процедура-обработчик вызывается самой формой локально. По крайней мере, у меня все работает без модификатора «Экспорт».
3 – естественно хотелось бы иметь возможность в нужных местах вызывать штатный обработчик, чтобы не дублировать его код в своем общем модуле. Но чтобы штатный обработчик можно было бы запустить извне формы, он должен быть экспортируемой функцией или в форме должна быть универсальная экспортная обертка для вызова штатных обработчиков.
2. Обдумаем увиденное
Исходя из всего вышесказанного, приходим к простым логическим выводам:
1 - инициализация механизма управления формой должна происходить в обработчике одного из первых событий в жизни формы, например, «Перед открытием», чтобы у нас было время для подмены всех нужных нам из числа остальных еще не вызванных обработчиков. Чтобы не менять код формы, можно внедриться в код какой-нибудь процедуры общего модуля, вызываемой при создании нужных нам форм.
2 – модуль формы должен содержать блок оберток универсального обработчика, которые будут вызываться нашим механизмом и в свою очередь будут вызывать универсальный (один для всех форм и их событий) обработчик, находящийся в нашем общем модуле. Отличаться друг от друга эти обертки будут только числом параметров, которое для личного комфорта можно отразить в названии этих процедур.
3 – модуль формы должен содержать универсальную экспортную процедуру-обертку для вызова произвольных локальных штатных процедур обработки событий формы и ее элементов.
3. Набросаем эскиз
Согласно этим умозаключениям модуль формы (пример из реально работающей и легко обслуживаемой конфигурации Комплексной Автоматизации на платформе 8.2) модифицируется следующим способом:
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
…
НашМодуль.НастроитьФормуДокумента(ДокументОбъект, ЭтаФорма);
КонецПроцедуры // ПередОткрытием()
…
//Универсальный блок+ ===============================================
Процедура УниверсальныйОбработчик0() {НашМодуль.УниверсальныйОбработчик(ЭтаФорма);}
Процедура УниверсальныйОбработчик1(П1) {НашМодуль.УниверсальныйОбработчик(ЭтаФорма, П1);}
Процедура УниверсальныйОбработчик2(П1, П2) {НашМодуль.УниверсальныйОбработчик(ЭтаФорма, П1, П2);}
Процедура УниверсальныйОбработчик3(П1, П2, П3) {НашМодуль.УниверсальныйОбработчик(ЭтаФорма, П1, П2, П3);}
Процедура УниверсальныйОбработчик4(П1, П2, П3, П4) { НашМодуль.УниверсальныйОбработчик(ЭтаФорма, П1, П2, П3, П4);}
Процедура ШтатныйОбработчик(Команда, П1, П2, П3, П4) Экспорт {Выполнить(Команда);}
//Универсальный блок- ===============================================
Наши вставки в штатный модуль формы выделены цветом, они во всех формах абсолютно одинаковы, никогда не меняются и легко автоматизируются.
Что я имею в виду под автоматизацией – возможность скриптом убрать эти изменения в модулях форм перед обновлением конфигурации и последующей их обратной вставки после обновления.
Выше я показал, что это необходимый минимум. Если этих изменений в формах хватит для реализации подписок на события форм и их элементов, то этот механизм будет самым компактным из всех возможных. Если бы что-то подобное было встроено в платформу, то настройка форм вообще не требовала бы их изменения.
4. Первоисточники
Идеи почерпнуты из статьи, в которой есть все необходимое для реализации описываемого механизма:
Tormozit , Методика переопределения и вызова обработчиков событий формы, http://kb.mista.ru/article.php?id=268.
Мной просто проделан дальнейший путь в направлении большей и, возможно, предельной универсальности этой методики.
5. Примеры использования механизма
Чтобы статья не превратилась в книжку, код реализации в простейшем случае – добавление своей кнопки в командную панель – прикладываю отдельным файлом.
Сфера применения данного механизма не ограничивается добавлением своих элементов на форму. С его помощью можно в зависимости от тех или иных условий, к примеру, визуально (цветом или шрифтом) выделять строки табличных частей, запрещать операции: проведения документа, создания других документов на его основании или редактирования отдельных его полей.
Более сложный пример – добавление «Табло цен» в табличную часть документов Товары. В отдельном табличном поле на форме для пользователей с определенными правами по добавленной в командную панель кнопке динамически выводится детальное описание расчета цены для номенклатуры, привязанное к текущей строке табличного поля товаров, – с выводом всех параметров, влияющих на расчет, например, региона покупателя, типа цен, размера наценки. А свободное пространство на форме для этого табло получается сжатием исходного табличного поля по горизонтали. При повторном нажатии на кнопку табло скрывается. И все это делается из общего модуля. Скриншот - в начале статьи.