Свой пункт в меню по правой кнопке: команды формы, кнопки и расширения

· 9 мин

Содержание

Чтобы программно добавить команду в контекстное меню 1С, в ПриСозданииНаСервере создают команду формы через Команды.Добавить(), а затем кнопку через Элементы.Добавить() с типом КнопкаФормы. Родителем кнопки указывают группу Элементы.<ИмяТаблицы>.КонтекстноеМеню. Обработчик команды — клиентская процедура в модуле формы, её имя записывают в свойство Действие.

Так работает любая управляемая форма платформы 1С:Предприятие 8.3, конфигурация роли не играет. В типовых базах код размещают в расширении, чтобы не снимать конфигурацию с поддержки. Тот же приём подходит и для меню поля ввода: у поля тоже есть своя группа КонтекстноеМеню, меняется только родитель кнопки.

Как устроено меню по правой кнопке в управляемой форме

У каждой таблицы формы (ТаблицаФормы) и у поля ввода есть свойство КонтекстноеМеню. Это группа формы особого вида — «Контекстное меню». Она живёт в дереве элементов формы как подчинённый элемент таблицы или поля, и в неё можно класть кнопки, как в обычную командную панель.

Из чего собирается меню, которое видит пользователь:

  • 📌 Стандартные команды таблицы: «Добавить», «Скопировать», «Изменить», «Удалить», поиск, вывод списка. Они появляются, когда у группы контекстного меню включено свойство Автозаполнение.
  • 🧩 Кнопки, добавленные разработчиком в редакторе формы — в конфигураторе или в расширении.
  • ⚙️ Кнопки, созданные кодом при открытии формы. Именно о них эта статья.

Кнопка сама по себе ничего не делает. Она ссылается на команду через свойство ИмяКоманды. Команда — отдельный объект: заголовок, картинка, сочетание клавиш и имя процедуры-обработчика. Одну команду можно вывести сразу в несколько мест: в командную панель, в контекстное меню таблицы и в меню поля.

Отдельного события «перед открытием контекстного меню» в управляемых формах нет. Меню собирается заранее, а его содержимое меняют через свойства кнопок Видимость и Доступность — обычно при смене текущей строки.

Какой способ выбрать

СпособКогда подходитРиски
Кнопка в редакторе формыСвоя конфигурация, состав меню постоянныйПравка типовой формы мешает обновлениям
Код в ПриСозданииНаСервереСостав команд зависит от прав, настроек, вида объектаОшибка в коде ломает открытие формы
Расширение конфигурацииТиповая конфигурация на поддержкеРасширение надо проверять после каждого обновления

Создаём команду формы и кнопку в контекстном меню кодом

Возьмём форму списка справочника с таблицей Список. Задача — добавить в меню по правой кнопке пункт «Отправить на проверку».

Шаг 1. Команда и кнопка при создании формы

Код пишут в обработчике ПриСозданииНаСервере, потому что Элементы.Добавить() и Команды.Добавить() доступны только на сервере.

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	ДобавитьКомандуВКонтекстноеМеню();
	
КонецПроцедуры

&НаСервере
Процедура ДобавитьКомандуВКонтекстноеМеню()
	
	ИмяКоманды = "ОтправитьНаПроверку";
	
	// Команда формы
	Команда = Команды.Добавить(ИмяКоманды);
	Команда.Заголовок = "Отправить на проверку";
	Команда.Подсказка = "Отметить выделенные элементы для проверки";
	Команда.Действие  = "ОтправитьНаПроверкуОбработка";
	
	// Кнопка в контекстном меню таблицы Список
	Кнопка = Элементы.Добавить("Кнопка" + ИмяКоманды, Тип("КнопкаФормы"),
		Элементы.Список.КонтекстноеМеню);
	Кнопка.ИмяКоманды = ИмяКоманды;
	
КонецПроцедуры

Свойство Действие — это строка с именем процедуры, а не ссылка на неё. Платформа ищет процедуру в модуле формы по имени, когда пользователь нажимает пункт.

Шаг 2. Место пункта в меню

Элементы.Добавить() ставит кнопку в конец группы. Чтобы пункт оказался выше стандартных команд, используют Элементы.Вставить() с четвёртым параметром — элементом, перед которым встаёт новый:

ПервыйЭлемент = Неопределено;
Если Элементы.Список.КонтекстноеМеню.ПодчиненныеЭлементы.Количество() > 0 Тогда
	ПервыйЭлемент = Элементы.Список.КонтекстноеМеню.ПодчиненныеЭлементы[0];
КонецЕсли;

Кнопка = Элементы.Вставить("Кнопка" + ИмяКоманды, Тип("КнопкаФормы"),
	Элементы.Список.КонтекстноеМеню, ПервыйЭлемент);

Если ПервыйЭлемент равен Неопределено, кнопка просто окажется в конце — ошибки не будет.

Шаг 3. Несколько команд одним циклом

Когда пунктов три-четыре, удобнее описать их структурой и создать в цикле. Имена кнопок должны быть уникальными в пределах формы, иначе Элементы.Добавить() вызовет исключение.

&НаСервере
Процедура ДобавитьКомандыВКонтекстноеМеню()
	
	Описания = Новый Массив;
	Описания.Добавить(Новый Структура("Имя, Заголовок", "ОтправитьНаПроверку", "Отправить на проверку"));
	Описания.Добавить(Новый Структура("Имя, Заголовок", "СнятьСПроверки", "Снять с проверки"));
	
	Для Каждого Описание Из Описания Цикл
		Команда = Команды.Добавить(Описание.Имя);
		Команда.Заголовок = Описание.Заголовок;
		Команда.Действие  = "КомандаКонтекстногоМенюОбработка";
		
		Кнопка = Элементы.Добавить("Кнопка" + Описание.Имя, Тип("КнопкаФормы"),
			Элементы.Список.КонтекстноеМеню);
		Кнопка.ИмяКоманды = Описание.Имя;
	КонецЦикла;
	
КонецПроцедуры

Здесь у всех команд один обработчик. Какая команда нажата, он узнаёт из параметра Команда по свойству Имя — пример в разделе про логику.

Управляем пунктами меню через события формы

Пункт часто нужен не всегда: например, «Отправить на проверку» имеет смысл только для непомеченных на удаление элементов. Для этого используют событие таблицы ПриАктивизацииСтроки.

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
	
	ПодключитьОбработчикОжидания("ОбновитьДоступностьКоманд", 0.2, Истина);
	
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьДоступностьКоманд()
	
	ТекущиеДанные = Элементы.Список.ТекущиеДанные;
	Доступна = ТекущиеДанные <> Неопределено
		И Не ТекущиеДанные.ПометкаУдаления;
	
	Элементы.КнопкаОтправитьНаПроверку.Доступность = Доступна;
	
КонецПроцедуры

Зачем обработчик ожидания: при прокрутке списка стрелками событие срабатывает на каждой строке. Задержка в 0,2 секунды и однократный вызов убирают лишнюю работу и подтормаживания.

Что ещё удобно делать в событиях формы:

  • 🔐 В ПриСозданииНаСервере — проверять права. Если у пользователя нет роли, кнопку не создают вовсе или ставят ей Видимость = Ложь.
  • 🔄 В ПриАктивизацииСтроки — менять Доступность и Заголовок кнопки в зависимости от строки.
  • 🧷 В ОбработкаОповещения — обновлять доступность после того, как другая форма изменила данные.

В ТекущиеДанные динамического списка видны только поля, которые есть в списке или в его запросе. Если обращаться к полю, которого там нет, будет ошибка. Нужное поле добавляют в запрос динамического списка или включают его использование в настройках списка.

Команда в контекстном меню через расширение конфигурации

В типовой БП 3.0, ЗУП 3.1 или УТ 11 менять форму напрямую невыгодно: каждое обновление превратится в ручное объединение. Расширение решает задачу без снятия с поддержки. Расширения появились в платформе 8.3.6, на актуальных версиях с формами в них работают без ограничений.

Вариант А. Без кода, в редакторе формы

  1. Откройте конфигуратор: Конфигурация → Расширения конфигурации, создайте расширение.
  2. В дереве основной конфигурации найдите нужную форму, в контекстном меню выберите Добавить в расширение.
  3. Откройте заимствованную форму в расширении, на вкладке «Команды» создайте команду и назначьте ей действие — платформа сама создаст процедуру в модуле формы расширения.
  4. Перетащите команду в дереве элементов на группу «Контекстное меню» под нужной таблицей.
  5. Сохраните расширение и проверьте в пользовательском режиме.

Вариант Б. Кодом в модуле расширенной формы

Здесь есть тонкость, о которую спотыкаются чаще всего. Аннотация &После("ПриСозданииНаСервере") работает, только если в исходной форме такой обработчик уже есть. Если его нет, обработчик события назначают в палитре свойств заимствованной формы расширения.

&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
	
	Команда = Команды.Добавить("Расш1_ОтправитьНаПроверку");
	Команда.Заголовок = "Отправить на проверку";
	Команда.Действие  = "Расш1_ОтправитьНаПроверкуОбработка";
	
	Кнопка = Элементы.Добавить("Расш1_КнопкаОтправитьНаПроверку", Тип("КнопкаФормы"),
		Элементы.Список.КонтекстноеМеню);
	Кнопка.ИмяКоманды = "Расш1_ОтправитьНаПроверку";
	
КонецПроцедуры

Процедура Расш1_ОтправитьНаПроверкуОбработка должна лежать в модуле формы расширения. Префикс Расш1_ в именах команд, кнопок и процедур защищает от совпадения с элементами, которые разработчики типовой могут добавить в следующих релизах.

В типовых на БСП печать, заполнение и создание на основании выводятся подсистемой «Подключаемые команды» и внешними обработками через «Дополнительные отчёты и обработки». Если пункт по смыслу относится к этим подменю, логичнее подключить его этими механизмами, а не строить кнопку руками.

Пишем обработчик: что делает команда после нажатия

Обработчик команды формы — клиентская процедура с одним параметром. Работу с базой выносят в серверную процедуру.

&НаКлиенте
Процедура КомандаКонтекстногоМенюОбработка(Команда)
	
	Выделенные = Элементы.Список.ВыделенныеСтроки;
	Если Выделенные.Количество() = 0 Тогда
		ПоказатьПредупреждение(, "Выделите хотя бы одну строку.");
		Возврат;
	КонецЕсли;
	
	НаПроверку = (Команда.Имя = "ОтправитьНаПроверку");
	Обработано = ИзменитьПризнакПроверки(Выделенные, НаПроверку);
	
	Элементы.Список.Обновить();
	ПоказатьОповещениеПользователя("Готово", ,
		СтрШаблон("Обработано элементов: %1", Формат(Обработано, "ЧГ=0")));
	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ИзменитьПризнакПроверки(Ссылки, НаПроверку)
	
	Обработано = 0;
	Для Каждого Ссылка Из Ссылки Цикл
		Объект = Ссылка.ПолучитьОбъект();
		Объект.НаПроверке = НаПроверку; // реквизит вашего справочника
		Объект.Записать();
		Обработано = Обработано + 1;
	КонецЦикла;
	
	Возврат Обработано;
	
КонецФункции

Почему так:

  • 📋 ВыделенныеСтроки у динамического списка возвращает массив ссылок. Пользователь может выделить с Shift или Ctrl несколько строк — команда обработает все.
  • 🖥️ &НаСервереБезКонтекста не гоняет данные формы на сервер. Для списка из сотен строк разница в скорости заметна.
  • 🔢 Формат(..., "ЧГ=0") убирает разделитель групп разрядов: без него 1500 превратится в «1 500».

Для таблицы значений или табличной части ВыделенныеСтроки возвращает идентификаторы строк, а не ссылки. Строку получают так: Объект.Товары.НайтиПоИдентификатору(Идентификатор).

Запись объектов из команды меняет рабочие данные — сначала проверьте её на копии базы. Если реквизит участвует в проведении документов, вместо простой записи может понадобиться перепроведение.

Отладка и проверка нового пункта

Код короткий, но мест, где он молча не работает , хватает. Порядок проверки:

  1. Поставьте точку останова на первой строке ПриСозданииНаСервере и запустите отладку (F5). Убедитесь, что процедура вообще вызывается — в расширении это главный подозреваемый.
  2. В табло посмотрите Элементы.Список.КонтекстноеМеню.ПодчиненныеЭлементы.Количество() до и после добавления кнопки.
  3. Поставьте точку останова в клиентском обработчике команды и нажмите пункт меню в пользовательском режиме.
  4. Проверьте поведение при выделении нескольких строк, пустом списке и строке, помеченной на удаление.
  5. Зайдите под пользователем с урезанными правами: пункт должен скрываться или не делать запрещённого.
Диагностика: выберите симптом

Если форма открывается заметно медленнее после доработки, запустите отладку и включите в конфигураторе Отладка → Замер производительности, затем откройте форму и посмотрите, сколько времени занимает ПриСозданииНаСервере.

Если форма обычная, а не управляемая

В старых конфигурациях на обычных формах (например, УПП 1.3 в толстом клиенте) объектная модель другая. Там у табличного поля свойство КонтекстноеМеню — это командная панель, и пункт добавляют через её коллекцию кнопок:

Меню = ЭлементыФормы.ТабличноеПоле1.КонтекстноеМеню;
Если Меню <> Неопределено Тогда
	Меню.Кнопки.Добавить("ОтправитьНаПроверку", ТипКнопкиКоманднойПанели.Действие,
		"Отправить на проверку", Новый Действие("ОтправитьНаПроверкуНажатие"));
КонецЕсли;

Обработчик — процедура модуля формы с параметром Кнопка. Если у поля своего меню нет, проще создать его в редакторе обычной формы, чем собирать кодом. Код управляемых форм (Команды.Добавить, КнопкаФормы) в обычных формах не работает, и наоборот.

Частые вопросы

Можно ли добавить пункт в контекстное меню всех форм сразу?
Нет, общего контекстного меню у платформы нет. Код добавляют в каждую нужную форму. Чтобы не дублировать его, выносят создание команд в общий серверный модуль и вызывают из ПриСозданииНаСервере каждой формы, передавая форму параметром.
Как добавить подменю в контекстное меню?
Создайте группу: Элементы.Добавить("ГруппаПроверка", Тип("ГруппаФормы"), Элементы.Список.КонтекстноеМеню), задайте ей Вид = ВидГруппыФормы.Подменю и заголовок. Кнопки затем добавляйте с родителем — этой группой.
Как назначить пункту горячую клавишу?
Через свойство команды СочетаниеКлавиш: Команда.СочетаниеКлавиш = Новый СочетаниеКлавиш(Клавиша.F9, , Истина) — это Ctrl+F9. Проверьте, что сочетание не занято стандартной командой таблицы.
Можно ли убрать стандартные пункты и оставить только свои?
Да. Выключите у группы контекстного меню Автозаполнение в редакторе формы или кодом. Стандартные команды пропадут, останутся только добавленные вами кнопки.
Сохранится ли кнопка после того, как пользователь настроит форму?
Кнопка, созданная кодом, создаётся заново при каждом открытии формы, поэтому она не зависит от того, что было в форме при прошлом открытии. Если пункт пропал у одного пользователя, сначала проверьте права и условия видимости в коде, затем сбросьте его настройки формы.