При разработке интерфейсов на управляемых формах программисты часто сталкиваются с задачей динамического изменения состава контекстного меню. Типичный сценарий: в зависимости от данных текущей строки списка нужно показать или скрыть определенные кнопки. Однако стандартный подход через событие ПриАктивизацииСтроки и изменение свойства Видимость кнопок часто приводит к неприятному эффекту «двойного клика». В этой статье мы подробно разберем, почему так происходит и какие существуют эффективные способы решения этой задачи.
Разберем ситуацию, с которой столкнулся автор темы. При нажатии правой кнопкой мыши (ПКМ) на строку списка платформа должна выполнить сразу несколько действий: активизировать строку, рассчитать состояние элементов формы и отрисовать само меню. Проблема заключается в том, что событие ПриАктивизацииСтроки и процесс отрисовки контекстного меню инициируются практически одновременно.
Проанализируем цепочку событий:
ПриАктивизацииСтроки, который делает серверный вызов для получения данных и меняет Видимость кнопок.В результате пользователь видит актуальное меню только при повторном нажатии правой кнопкой мыши. Это создает ощущение нестабильной работы интерфейса.
Наиболее профессиональным и надежным способом обхода ограничений стандартного контекстного меню является использование метода ПоказатьВыборИзМеню. Вместо того чтобы пытаться управлять видимостью заранее созданных кнопок в стандартном меню, мы можем полностью перехватить процесс формирования списка команд в момент обращения пользователя.
Рассмотрим подробнее, как это реализовать. Вместо использования стандартного контекстного меню элемента формы, мы можем создать свою команду (например, кнопку в командной панели или гиперссылку), которая будет вызывать список доступных действий.
Посмотрим на пример кода, который формирует динамическое меню «на лету»:
&НаКлиенте
Процедура КомандаДействияСписка(Команда)
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Формируем список доступных действий динамически
СписокВыбора = Новый СписокЗначений;
// Здесь может быть логика проверки данных строки
Если ТекущиеДанные.Статус = "Новый" Тогда
СписокВыбора.Добавить("УстановитьСтатусВРаботе", "Взять в работу");
ИначеЕсли ТекущиеДанные.Статус = "ВРаботе" Тогда
СписокВыбора.Добавить("Завершить", "Завершить выполнение");
КонецЕсли;
// Добавляем постоянные пункты
СписокВыбора.Добавить("ПросмотрИстории", "История изменений...");
// Показываем меню прямо под курсором или кнопкой
Оповещение = Новый ОписаниеОповещения("ОбработатьВыборМеню", ЭтотОбъект);
ПоказатьВыборИзМеню(Оповещение, СписокВыбора);
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьВыборМеню(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт
Если ВыбранныйЭлемент = Неопределено Тогда
Возврат;
КонецЕсли;
ИмяКоманды = ВыбранныйЭлемент.Значение;
// Выполняем соответствующее действие
Если ИмяКоманды = "УстановитьСтатусВРаботе" Тогда
// Вызов серверной процедуры обработки
КонецЕсли;
КонецПроцедуры
Этот метод гарантирует, что пользователь всегда увидит актуальный набор кнопок, так как меню строится непосредственно в момент вызова, а не заранее.
Если специфика задачи требует использования именно стандартного контекстного меню, попробуем минимизировать визуальный дискомфорт. Как было отмечено в обсуждении, изменение свойства Видимость заставляет платформу пересчитывать геометрию формы и «перерисовывать» состав элементов, что часто сопровождается мерцанием или прыжками интерфейса.
Проанализируем ситуацию: если мы будем менять Доступность (Enabled) кнопок, то состав меню останется неизменным (все 5 кнопок будут присутствовать всегда), но неактивные кнопки станут серыми. Это не решает проблему задержки обновления при первом клике, но избавляет от резкого изменения размеров меню после того, как серверный вызов завершится. Однако, стоит признать, что для динамических списков с большим количеством условий этот метод все равно остается компромиссным.
Рассмотрим вариант, к которому в итоге пришел автор вопроса. Если количество связанных записей невелико и их нужно просто выбрать для дальнейшего действия, можно создать одну фиксированную кнопку в контекстном меню (например, «Связанные документы» или «Дополнительные действия»).
При нажатии на эту кнопку программно вызывается форма выбора или модальный список. Выясним преимущества этого подхода:
Для реализации можно использовать метод ВыбратьЭлемент у объекта СписокЗначений:
&НаКлиенте
Процедура ВыполнитьСвязанноеДействие(Команда)
// Получаем данные с сервера только по запросу
СписокДействий = ПолучитьСписокДействийНаСервере(Элементы.Список.ТекущаяСтрока);
Если СписокДействий.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Нет доступных действий для данной строки");
Возврат;
КонецЕсли;
ВыбранноеДействие = СписокДействий.ВыбратьЭлемент("Выберите действие");
Если ВыбранноеДействие <> Неопределено Тогда
// Обработка выбора
КонецЕсли;
КонецПроцедуры
Важно помнить, что любые манипуляции в событии ПриАктивизацииСтроки, требующие обращения к базе данных, создают серьезную нагрузку. Если пользователь быстро пролистывает список клавишами «вверх»/«вниз», каждое перемещение инициирует серверный вызов. Это может привести к «фризам» интерфейса.
Чтобы оптимизировать работу, проанализируем следующие рекомендации:
Элементы.ИмяСписка.ТекущиеДанные без обращения к серверу.ПодключитьОбработчикОжидания с небольшой задержкой (0.1 - 0.2 сек), чтобы не спамить запросами при быстром пролистывании. Однако помните, что это еще больше увеличивает вероятность «эффекта старого меню» при клике ПКМ.Подводя итог, можно сказать, что платформа 1С:Предприятие 8 не проектировалась для мгновенной динамической перестройки системных окон, таких как контекстное меню, по событию клика. Наилучшим решением будет отказ от изменения видимости кнопок стандартного меню в пользу использования метода ПоказатьВыборИзМеню или создания фиксированной кнопки, открывающей список подбора. Это обеспечит предсказуемость интерфейса и высокую скорость работы приложения.