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