Свой пункт в правой кнопке мыши таблицы управляемой формы
Содержание
Чтобы добавить команду в контекстное меню табличной части в 1С, создайте команду формы, напишите для неё обработчик и перетащите команду в элемент ТаблицаКонтекстноеМеню на закладке «Элементы» конфигуратора. Пункт появится по правому клику на строке таблицы. Всё это относится к управляемым формам платформы 8.3 с интерфейсом «Такси».
Такая команда нужна, когда пользователю надо сделать что-то со строкой или группой строк прямо на месте: заполнить цену, открыть связанный документ, пересчитать сумму. Ниже путь от создания команды до работы с выделением и сервером, плюс способ добавить пункт кодом, если форма типовая и менять её в конфигураторе нельзя.
С чего начать: что за таблица и где живёт форма
Прежде чем что-то создавать, выясните три вещи. От них зависит, как писать обработчик.
- Источник данных таблицы. Табличная часть документа или реквизит формы типа «Таблица значений» — это данные формы. Динамический список — это запрос к базе, а не коллекция в памяти.
- Чья форма. Своя обработка или объект вашей конфигурации правятся напрямую. Форму типовой конфигурации (БП 3.0, ЗУП 3.1, УТ 11), как правило, на поддержке: менять её в лоб не стоит.
- Где команда должна быть видна. Только в контекстном меню или ещё и в командной панели над таблицей.
У каждой таблицы на форме есть собственный элемент контекстного меню. Его имя строится как имя таблицы плюс КонтекстноеМеню: для таблицы Товары это ТоварыКонтекстноеМеню.
Если форма типовая, делайте это через расширение конфигурации (появились в 8.3.6): заимствуйте форму в расширение и правьте уже её. Расширение подключается отдельным шагом — после загрузки .cfe нужно обновить конфигурацию
базы данных.
Создание команды формы
Откройте форму в конфигураторе и перейдите на закладку «Команды». Нажмите «Добавить» и заполните свойства.
| Свойство | Что указать |
|---|---|
| Имя | Латиница или кириллица без пробелов, например ЗаполнитьЦену |
| Заголовок | То, что увидит пользователь: «Заполнить цену» |
| Действие | Имя процедуры-обработчика; кнопка с лупой создаст заготовку |
| Подсказка | Короткое пояснение при наведении мыши |
| Картинка | По желанию, из библиотеки или своя |
Обработчик команды делайте клиентским. Тогда он видит выделение в таблице, а на сервер вы уйдёте отдельным вызовом.
Заготовка получится такой:
&НаКлиенте
Процедура ЗаполнитьЦену(Команда)
// здесь будет логика
КонецПроцедуры
Как вывести команду в контекстное меню таблицы
Теперь команду надо показать. Способ самый короткий.
- Откройте закладку «Элементы» и найдите свою таблицу.
- Раскройте узел таблицы: внутри есть «Контекстное меню» (элемент
ТаблицаКонтекстноеМеню) и «Командная панель». - Перетащите команду с закладки «Команды» в узел контекстного меню.
- Запустите базу и проверьте правый клик по строке.
Платформа сама создаст кнопку формы, привязанную к команде. Кнопке можно задать свой заголовок, отличный от заголовка команды, и порядок среди других пунктов.
Одну команду допустимо вывести сразу в два места: в контекстное меню и в командную панель. Для этого перетащите её в оба узла. Обработчик останется один.
Если меню таблицы нет в дереве
Бывает, что контекстное меню у таблицы скрыто или его вообще не видно в дереве. Проверьте свойство таблицы «Контекстное меню»: оно не должно быть отключено. Для таблиц, созданных программно, меню создаётся автоматически, но обращаться к нему надо по имени.
Настройка отображения: порядок, группы и видимость
Когда пунктов больше двух-трёх, меню нужно упорядочить.
- Положение пункта задаётся порядком кнопок в дереве элементов. Выше в дереве — выше в меню.
- Логически связанные команды собирайте в группу «Подменю» или разделяйте группой без заголовка: получится линия-разделитель.
- Картинку ставьте только тем пунктам, которыми пользуются часто. Меню, где у каждого пункта своя иконка, читается хуже.
Видимостью и доступностью управляет код. Типичная задача: пункт нужен только для строк определённого вида. Тогда в обработчике события таблицы ПриАктивизацииСтроки меняйте свойство Доступность кнопки:
&НаКлиенте
Процедура ТоварыПриАктивизацииСтроки(Элемент)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Элементы.ТоварыКонтекстноеМенюЗаполнитьЦену.Доступность =
(ТекущиеДанные <> Неопределено И Не ТекущиеДанные.ЦенаЗафиксирована);
КонецПроцедуры
Имя кнопки платформа формирует сама: имя меню плюс имя команды. Точное имя смотрите в дереве элементов, а не угадывайте.
Если пользователь не должен выполнять действие вообще, одной недоступности пункта мало. Права проверяйте на сервере: меню — это удобство, а не защита.
Обработчик нажатия: текущая строка и выделенные строки
Самая частая ошибка новичка — взять не то, что нужно. Это два разных свойства таблицы.
| Свойство | Что даёт | Когда использовать |
|---|---|---|
ТекущиеДанные | Одна активная строка, либо Неопределено, если строки нет | Действие над одной строкой |
ВыделенныеСтроки | Массив идентификаторов всех выделенных строк | Групповая операция |
Для одной строки:
&НаКлиенте
Процедура ЗаполнитьЦену(Команда)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ТекущиеДанные.Цена = 100;
ТекущиеДанные.Сумма = ТекущиеДанные.Цена * ТекущиеДанные.Количество;
КонецПроцедуры
Проверка на Неопределено обязательна. Таблица может быть пустой, и тогда обращение к полю строки даст ошибку «Поле объекта не обнаружено».
Для нескольких строк проходите по массиву идентификаторов и получайте строку через коллекцию формы:
&НаКлиенте
Процедура ЗаполнитьЦену(Команда)
Для Каждого ИдСтроки Из Элементы.Товары.ВыделенныеСтроки Цикл
Строка = Объект.Товары.НайтиПоИдентификатору(ИдСтроки);
Строка.Цена = 100;
Строка.Сумма = Строка.Цена * Строка.Количество;
КонецЦикла;
КонецПроцедуры
Объект.Товары здесь — табличная часть документа. Если у вас реквизит формы, пишите Товары.НайтиПоИдентификатору(...).
Особенность динамического списка
В динамическом списке строк в памяти нет, есть данные из запроса. Поэтому ВыделенныеСтроки вернёт не номера строк, а значения ключа — для справочника или документа это ссылки. Берите их и работайте через сервер:
&НаКлиенте
Процедура ПровестиВыбранные(Команда)
Для Каждого Ссылка Из Элементы.Список.ВыделенныеСтроки Цикл
ПровестиДокументНаСервере(Ссылка);
КонецЦикла;
КонецПроцедуры
Вызывать сервер в цикле для каждого документа дорого. Лучше передать весь массив одним вызовом: ПровестиДокументыНаСервере(Элементы.Список.ВыделенныеСтроки).
В динамическом списке могут попасться строки-группы иерархии и строки группировки. Для них ключ — не ссылка на элемент. Если команда применима только к элементам, отфильтруйте их.
Передача данных на сервер и обновление интерфейса
Клиентская процедура не читает и не пишет в базу. Для этого нужен серверный вызов. В обработчике соберите данные и передайте их параметрами.
&НаКлиенте
Процедура УстановитьСтатус(Команда)
Выделенные = Элементы.Список.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
Возврат;
КонецЕсли;
УстановитьСтатусНаСервере(Выделенные);
Элементы.Список.Обновить();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура УстановитьСтатусНаСервере(МассивСсылок)
Для Каждого Ссылка Из МассивСсылок Цикл
Объект = Ссылка.ПолучитьОбъект();
Объект.Статус = Перечисления.СтатусыЗаказов.Выполнен;
Объект.Записать();
КонецЦикла;
КонецПроцедуры
Здесь два приёма, о которых забывают.
- Директива
&НаСервереБезКонтекстане тянет с клиента весь контекст формы. Вызов быстрее, но обращаться к реквизитам формы внутри нельзя. Если данные формы нужны — берите обычный&НаСервере. Элементы.Список.Обновить()перечитывает динамический список. Для таблицы-коллекции этого не требуется: изменения вОбъект.Товарыотображаются сразу.
Если серверная процедура меняет табличную часть документа, который открыт в форме, данные формы обновятся автоматически, так как изменения идут в реквизит формы. Записывать документ при этом не обязательно: он останется изменённым до нажатия «Записать».
Групповое изменение через Записать() пропускает проверки, которые есть у форм документов. Для документов с движениями используйте Записать(РежимЗаписиДокумента.Проведение) только если вы уверены в правилах учёта, и предупредите пользователя об изменении данных.
Мини-пример: копирование строки с новым количеством
Для закрепления соберём задачу целиком. Нужен пункт «Дублировать строку» в таблице Товары документа. По клику создаётся копия активной строки, а курсор переходит на неё.
- На закладке «Команды» создайте команду
ДублироватьСтроку, заголовок «Дублировать строку», действие — одноимённая процедура. - Перетащите команду в
ТоварыКонтекстноеМеню. - Напишите обработчик.
&НаКлиенте
Процедура ДублироватьСтроку(Команда)
Текущая = Элементы.Товары.ТекущиеДанные;
Если Текущая = Неопределено Тогда
Возврат;
КонецЕсли;
Новая = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(Новая, Текущая);
Элементы.Товары.ТекущаяСтрока = Новая.ПолучитьИдентификатор();
КонецПроцедуры
ЗаполнитьЗначенияСвойств переносит все совпадающие по имени колонки, а ПолучитьИдентификатор даёт то, что принимает свойство ТекущаяСтрока. Копия окажется в конце таблицы. Чтобы вставить её сразу после исходной, используйте Вставить с индексом на единицу больше: Объект.Товары.Вставить(Объект.Товары.Индекс(Текущая) + 1). Индекс строки считайте в момент нажатия, а не заранее.
Итоговую сумму документа, если она хранится отдельным реквизитом, пересчитайте после вставки. Платформа сама этого не сделает.
Что проверить перед сдачей
Прежде чем отдавать форму пользователям, пройдите короткую проверку.
- Нажмите пункт на пустой таблице: форма не должна падать с ошибкой.
- Выделите одну строку, потом несколько через
Ctrlи диапазон черезShift. Групповая команда должна отработать на каждой. - Откройте форму под пользователем с ограниченными правами. Если команда пишет в базу, а прав нет, пользователь должен получить понятное сообщение, а не технический текст ошибки.
- Закройте форму без записи и откройте снова: изменения, которые команда внесла в несохранённый документ, не должны остаться в базе.
- Проверьте скорость на большом списке. Команда над сотней строк, которая вызывает сервер в цикле, зависнет на заметное время. Один вызов с массивом справляется на порядок быстрее.
Если пункт меню нужен нескольким таблицам формы, не копируйте обработчик. Создайте одну команду и выведите её в контекстное меню каждой таблицы, а внутри определяйте таблицу по ТекущийЭлемент. Так правка логики делается в одном месте.
Когда менять форму нельзя: добавить пункт кодом
В типовых конфигурациях форма на поддержке, а команду добавить надо. Расширение решает задачу так: в нём перехватывается ПриСоздатьНаСервере, и пункт создаётся программно. Элементы и команды формы добавляются только на сервере.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Команда = Команды.Добавить("ЗаполнитьЦену");
Команда.Заголовок = "Заполнить цену";
Команда.Действие = "ЗаполнитьЦенуОбработчик";
Кнопка = Элементы.Добавить("ТоварыКонтекстноеМенюЗаполнитьЦену",
Тип("КнопкаФормы"), Элементы.ТоварыКонтекстноеМеню);
Кнопка.ИмяКоманды = "ЗаполнитьЦену";
КонецПроцедуры
Обработчик с именем из свойства Действие должен существовать в модуле формы и быть клиентским (&НаКлиенте) с одним параметром. Если имя написано с опечаткой, ошибка выявится только при нажатии.
У такого способа есть цена. Платформа не покажет добавленный элемент в конфигураторе, и новому разработчику будет непросто найти, откуда он взялся. Оставляйте комментарий над блоком и держите такие вставки в одном месте.
Типичные ошибки при добавлении команды
Если пункт не появился или ведёт себя странно, проверьте по списку.
- Пункта нет в меню. Команду создали, но не перетащили на
ТаблицаКонтекстноеМеню. Закладка «Команды» сама ничего не показывает. - Нажатие ничего не делает. В свойстве «Действие» пусто или указана не та процедура. Обработчик найдите по имени из свойства.
- «Поле объекта не обнаружено». Обращение к
ТекущиеДанныебез проверки наНеопределенолибо неверное имя колонки. - Процедура не найдена на сервере. Вызов серверной процедуры из неё самой, а не из клиентской. Клиент вызывает сервер, обратно нельзя.
- В динамическом списке обрабатываются не те объекты. Ключом считали номер строки, а пришла ссылка.
- Меню на своей форме есть, на типовой пропало. После обновления конфигурации поддержки форма переписана. Проверьте, что правка сделана в расширении, а не в основной конфигурации.
Для отладки поставьте точку останова на первой строке обработчика и запустите базу в режиме отладки. В отладчике сразу видно, что вернул ТекущиеДанные и какие значения лежат в ВыделенныеСтроки. Часто проблема находится за минуту.
Частые вопросы
Куда в конфигураторе добавляется команда, чтобы она попала в контекстное меню?
Можно ли сделать так, чтобы пункт был виден не всегда?
Видимость или Доступность кнопки в обработчике ПриАктивизацииСтроки или при открытии формы, исходя из данных текущей строки.Как получить сразу все выделенные строки?
Элементы.Таблица.ВыделенныеСтроки. Это массив идентификаторов, для таблицы-коллекции строку находит НайтиПоИдентификатору. В динамическом списке в массиве лежат ключи.Можно ли добавить пункт в типовую форму, не меняя конфигурацию поддержки?
ПриСозданииНаСервере.Почему элемент нельзя добавить в клиентской процедуре?
Элементы.Добавить и Команды.Добавить работают в серверных процедурах.