Свой пункт в правой кнопке мыши таблицы управляемой формы

· 9 мин

Содержание

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

Такая команда нужна, когда пользователю надо сделать что-то со строкой или группой строк прямо на месте: заполнить цену, открыть связанный документ, пересчитать сумму. Ниже путь от создания команды до работы с выделением и сервером, плюс способ добавить пункт кодом, если форма типовая и менять её в конфигураторе нельзя.

С чего начать: что за таблица и где живёт форма

Прежде чем что-то создавать, выясните три вещи. От них зависит, как писать обработчик.

  • Источник данных таблицы. Табличная часть документа или реквизит формы типа «Таблица значений» — это данные формы. Динамический список — это запрос к базе, а не коллекция в памяти.
  • Чья форма. Своя обработка или объект вашей конфигурации правятся напрямую. Форму типовой конфигурации (БП 3.0, ЗУП 3.1, УТ 11), как правило, на поддержке: менять её в лоб не стоит.
  • Где команда должна быть видна. Только в контекстном меню или ещё и в командной панели над таблицей.

У каждой таблицы на форме есть собственный элемент контекстного меню. Его имя строится как имя таблицы плюс КонтекстноеМеню: для таблицы Товары это ТоварыКонтекстноеМеню.

Если форма типовая, делайте это через расширение конфигурации (появились в 8.3.6): заимствуйте форму в расширение и правьте уже её. Расширение подключается отдельным шагом — после загрузки .cfe нужно обновить конфигурацию базы данных.

Создание команды формы

Откройте форму в конфигураторе и перейдите на закладку «Команды». Нажмите «Добавить» и заполните свойства.

СвойствоЧто указать
ИмяЛатиница или кириллица без пробелов, например ЗаполнитьЦену
ЗаголовокТо, что увидит пользователь: «Заполнить цену»
ДействиеИмя процедуры-обработчика; кнопка с лупой создаст заготовку
ПодсказкаКороткое пояснение при наведении мыши
КартинкаПо желанию, из библиотеки или своя

Обработчик команды делайте клиентским. Тогда он видит выделение в таблице, а на сервер вы уйдёте отдельным вызовом.

Заготовка получится такой:

&НаКлиенте
Процедура ЗаполнитьЦену(Команда)
	// здесь будет логика
КонецПроцедуры

Как вывести команду в контекстное меню таблицы

Теперь команду надо показать. Способ самый короткий.

  1. Откройте закладку «Элементы» и найдите свою таблицу.
  2. Раскройте узел таблицы: внутри есть «Контекстное меню» (элемент ТаблицаКонтекстноеМеню) и «Командная панель».
  3. Перетащите команду с закладки «Команды» в узел контекстного меню.
  4. Запустите базу и проверьте правый клик по строке.

Платформа сама создаст кнопку формы, привязанную к команде. Кнопке можно задать свой заголовок, отличный от заголовка команды, и порядок среди других пунктов.

Одну команду допустимо вывести сразу в два места: в контекстное меню и в командную панель. Для этого перетащите её в оба узла. Обработчик останется один.

Если меню таблицы нет в дереве

Бывает, что контекстное меню у таблицы скрыто или его вообще не видно в дереве. Проверьте свойство таблицы «Контекстное меню»: оно не должно быть отключено. Для таблиц, созданных программно, меню создаётся автоматически, но обращаться к нему надо по имени.

Настройка отображения: порядок, группы и видимость

Когда пунктов больше двух-трёх, меню нужно упорядочить.

  • Положение пункта задаётся порядком кнопок в дереве элементов. Выше в дереве — выше в меню.
  • Логически связанные команды собирайте в группу «Подменю» или разделяйте группой без заголовка: получится линия-разделитель.
  • Картинку ставьте только тем пунктам, которыми пользуются часто. Меню, где у каждого пункта своя иконка, читается хуже.

Видимостью и доступностью управляет код. Типичная задача: пункт нужен только для строк определённого вида. Тогда в обработчике события таблицы ПриАктивизацииСтроки меняйте свойство Доступность кнопки:

&НаКлиенте
Процедура ТоварыПриАктивизацииСтроки(Элемент)
	ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
	Элементы.ТоварыКонтекстноеМенюЗаполнитьЦену.Доступность =
		(ТекущиеДанные <> Неопределено И Не ТекущиеДанные.ЦенаЗафиксирована);
КонецПроцедуры

Имя кнопки платформа формирует сама: имя меню плюс имя команды. Точное имя смотрите в дереве элементов, а не угадывайте.

Если пользователь не должен выполнять действие вообще, одной недоступности пункта мало. Права проверяйте на сервере: меню — это удобство, а не защита.

Обработчик нажатия: текущая строка и выделенные строки

Самая частая ошибка новичка — взять не то, что нужно. Это два разных свойства таблицы.

СвойствоЧто даётКогда использовать
ТекущиеДанныеОдна активная строка, либо Неопределено, если строки нетДействие над одной строкой
ВыделенныеСтрокиМассив идентификаторов всех выделенных строкГрупповая операция

Для одной строки:

&НаКлиенте
Процедура ЗаполнитьЦену(Команда)
	ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;
	ТекущиеДанные.Цена = 100;
	ТекущиеДанные.Сумма = ТекущиеДанные.Цена * ТекущиеДанные.Количество;
КонецПроцедуры

Проверка на Неопределено обязательна. Таблица может быть пустой, и тогда обращение к полю строки даст ошибку «Поле объекта не обнаружено».

Для нескольких строк проходите по массиву идентификаторов и получайте строку через коллекцию формы:

&НаКлиенте
Процедура ЗаполнитьЦену(Команда)
	Для Каждого ИдСтроки Из Элементы.Товары.ВыделенныеСтроки Цикл
		Строка = Объект.Товары.НайтиПоИдентификатору(ИдСтроки);
		Строка.Цена = 100;
		Строка.Сумма = Строка.Цена * Строка.Количество;
	КонецЦикла;
КонецПроцедуры

Объект.Товары здесь — табличная часть документа. Если у вас реквизит формы, пишите Товары.НайтиПоИдентификатору(...).

Особенность динамического списка

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

&НаКлиенте
Процедура ПровестиВыбранные(Команда)
	Для Каждого Ссылка Из Элементы.Список.ВыделенныеСтроки Цикл
		ПровестиДокументНаСервере(Ссылка);
	КонецЦикла;
КонецПроцедуры

Вызывать сервер в цикле для каждого документа дорого. Лучше передать весь массив одним вызовом: ПровестиДокументыНаСервере(Элементы.Список.ВыделенныеСтроки).

В динамическом списке могут попасться строки-группы иерархии и строки группировки. Для них ключ — не ссылка на элемент. Если команда применима только к элементам, отфильтруйте их.

Передача данных на сервер и обновление интерфейса

Клиентская процедура не читает и не пишет в базу. Для этого нужен серверный вызов. В обработчике соберите данные и передайте их параметрами.

&НаКлиенте
Процедура УстановитьСтатус(Команда)
	Выделенные = Элементы.Список.ВыделенныеСтроки;
	Если Выделенные.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	УстановитьСтатусНаСервере(Выделенные);
	Элементы.Список.Обновить();
КонецПроцедуры

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

Здесь два приёма, о которых забывают.

  • Директива &НаСервереБезКонтекста не тянет с клиента весь контекст формы. Вызов быстрее, но обращаться к реквизитам формы внутри нельзя. Если данные формы нужны — берите обычный &НаСервере.
  • Элементы.Список.Обновить() перечитывает динамический список. Для таблицы-коллекции этого не требуется: изменения в Объект.Товары отображаются сразу.

Если серверная процедура меняет табличную часть документа, который открыт в форме, данные формы обновятся автоматически, так как изменения идут в реквизит формы. Записывать документ при этом не обязательно: он останется изменённым до нажатия «Записать».

Групповое изменение через Записать() пропускает проверки, которые есть у форм документов. Для документов с движениями используйте Записать(РежимЗаписиДокумента.Проведение) только если вы уверены в правилах учёта, и предупредите пользователя об изменении данных.

Мини-пример: копирование строки с новым количеством

Для закрепления соберём задачу целиком. Нужен пункт «Дублировать строку» в таблице Товары документа. По клику создаётся копия активной строки, а курсор переходит на неё.

  1. На закладке «Команды» создайте команду ДублироватьСтроку, заголовок «Дублировать строку», действие — одноимённая процедура.
  2. Перетащите команду в ТоварыКонтекстноеМеню.
  3. Напишите обработчик.
&НаКлиенте
Процедура ДублироватьСтроку(Команда)
	Текущая = Элементы.Товары.ТекущиеДанные;
	Если Текущая = Неопределено Тогда
		Возврат;
	КонецЕсли;
	Новая = Объект.Товары.Добавить();
	ЗаполнитьЗначенияСвойств(Новая, Текущая);
	Элементы.Товары.ТекущаяСтрока = Новая.ПолучитьИдентификатор();
КонецПроцедуры

ЗаполнитьЗначенияСвойств переносит все совпадающие по имени колонки, а ПолучитьИдентификатор даёт то, что принимает свойство ТекущаяСтрока. Копия окажется в конце таблицы. Чтобы вставить её сразу после исходной, используйте Вставить с индексом на единицу больше: Объект.Товары.Вставить(Объект.Товары.Индекс(Текущая) + 1). Индекс строки считайте в момент нажатия, а не заранее.

Итоговую сумму документа, если она хранится отдельным реквизитом, пересчитайте после вставки. Платформа сама этого не сделает.

Что проверить перед сдачей

Прежде чем отдавать форму пользователям, пройдите короткую проверку.

  • Нажмите пункт на пустой таблице: форма не должна падать с ошибкой.
  • Выделите одну строку, потом несколько через Ctrl и диапазон через Shift. Групповая команда должна отработать на каждой.
  • Откройте форму под пользователем с ограниченными правами. Если команда пишет в базу, а прав нет, пользователь должен получить понятное сообщение, а не технический текст ошибки.
  • Закройте форму без записи и откройте снова: изменения, которые команда внесла в несохранённый документ, не должны остаться в базе.
  • Проверьте скорость на большом списке. Команда над сотней строк, которая вызывает сервер в цикле, зависнет на заметное время. Один вызов с массивом справляется на порядок быстрее.

Если пункт меню нужен нескольким таблицам формы, не копируйте обработчик. Создайте одну команду и выведите её в контекстное меню каждой таблицы, а внутри определяйте таблицу по ТекущийЭлемент. Так правка логики делается в одном месте.

Когда менять форму нельзя: добавить пункт кодом

В типовых конфигурациях форма на поддержке, а команду добавить надо. Расширение решает задачу так: в нём перехватывается ПриСоздатьНаСервере, и пункт создаётся программно. Элементы и команды формы добавляются только на сервере.

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	Команда = Команды.Добавить("ЗаполнитьЦену");
	Команда.Заголовок = "Заполнить цену";
	Команда.Действие = "ЗаполнитьЦенуОбработчик";

	Кнопка = Элементы.Добавить("ТоварыКонтекстноеМенюЗаполнитьЦену",
		Тип("КнопкаФормы"), Элементы.ТоварыКонтекстноеМеню);
	Кнопка.ИмяКоманды = "ЗаполнитьЦену";
КонецПроцедуры

Обработчик с именем из свойства Действие должен существовать в модуле формы и быть клиентским (&НаКлиенте) с одним параметром. Если имя написано с опечаткой, ошибка выявится только при нажатии.

У такого способа есть цена. Платформа не покажет добавленный элемент в конфигураторе, и новому разработчику будет непросто найти, откуда он взялся. Оставляйте комментарий над блоком и держите такие вставки в одном месте.

Типичные ошибки при добавлении команды

Если пункт не появился или ведёт себя странно, проверьте по списку.

  • Пункта нет в меню. Команду создали, но не перетащили на ТаблицаКонтекстноеМеню. Закладка «Команды» сама ничего не показывает.
  • Нажатие ничего не делает. В свойстве «Действие» пусто или указана не та процедура. Обработчик найдите по имени из свойства.
  • «Поле объекта не обнаружено». Обращение к ТекущиеДанные без проверки на Неопределено либо неверное имя колонки.
  • Процедура не найдена на сервере. Вызов серверной процедуры из неё самой, а не из клиентской. Клиент вызывает сервер, обратно нельзя.
  • В динамическом списке обрабатываются не те объекты. Ключом считали номер строки, а пришла ссылка.
  • Меню на своей форме есть, на типовой пропало. После обновления конфигурации поддержки форма переписана. Проверьте, что правка сделана в расширении, а не в основной конфигурации.

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

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

Куда в конфигураторе добавляется команда, чтобы она попала в контекстное меню?
В узел «Контекстное меню» таблицы на закладке «Элементы». Просто создать команду на закладке «Команды» мало.
Можно ли сделать так, чтобы пункт был виден не всегда?
Да. Меняйте Видимость или Доступность кнопки в обработчике ПриАктивизацииСтроки или при открытии формы, исходя из данных текущей строки.
Как получить сразу все выделенные строки?
Через Элементы.Таблица.ВыделенныеСтроки. Это массив идентификаторов, для таблицы-коллекции строку находит НайтиПоИдентификатору. В динамическом списке в массиве лежат ключи.
Можно ли добавить пункт в типовую форму, не меняя конфигурацию поддержки?
Да, через расширение конфигурации: заимствуйте форму и добавьте команду там либо создайте её программно в ПриСозданииНаСервере.
Почему элемент нельзя добавить в клиентской процедуре?
Структура формы меняется только на сервере: Элементы.Добавить и Команды.Добавить работают в серверных процедурах.