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