Разделители в контекстном меню при скрытии MenuItem

Автор Тема: Разделители в контекстном меню при скрытии MenuItem  (Прочитано 6734 раз)

0 Пользователей и 5 Гостей просматривают эту тему.

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Всем привет. При создании контекстного меню для объекта есть лайфхак по скрытию пункта меню, если объект не подходит по условиям.
При этом есть два варианта:
1. Первый - как в ссылке - установить значение Enable = false. При этом пункт в меню будет виден, но не будет доступен. Не подходит.
2. Второй - установить Visible = false. При этом пункт меню скрывается. Подходит.
Итак, пытаемся реализовать:
Код - C# [Выбрать]
  1. private static void AttachCreateAnalogContextMenu()
  2. {
  3.     if (_intellectualEntityContextMenu == null)
  4.     {
  5.         _intellectualEntityContextMenu = new ContextMenuExtension();
  6.         //todo translate
  7.         var menuItem = new Autodesk.AutoCAD.Windows.MenuItem("MP:Create analog");
  8.         menuItem.Click += CreateAnalogMenuItem_Click;
  9.         _intellectualEntityContextMenu.MenuItems.Add(menuItem);
  10.         _intellectualEntityContextMenu.MenuItems.Add(new Autodesk.AutoCAD.Windows.MenuItem("MenuItem2"));
  11.         _intellectualEntityContextMenu.MenuItems.Add(new Autodesk.AutoCAD.Windows.MenuItem("MenuItem3"));
  12.         _intellectualEntityContextMenu.Popup += IntellectualEntityContextMenuPopup;
  13.  
  14.         var rxObject = RXObject.GetClass(typeof(BlockReference));
  15.         Autodesk.AutoCAD.ApplicationServices.Application.AddObjectContextMenuExtension(rxObject, _intellectualEntityContextMenu);
  16.     }
  17. }
  18.  
  19. private static void IntellectualEntityContextMenuPopup(object sender, EventArgs e)
  20. {
  21.     if (sender is ContextMenuExtension contextMenuExtension)
  22.     {
  23.         try
  24.         {
  25.             var selectionResult = AcadHelpers.Editor.SelectImplied();
  26.             var menuEnable = false;
  27.             if (selectionResult.Status == PromptStatus.OK &&
  28.                 selectionResult.Value.Count == 1)
  29.             {
  30.                 using (var tr = AcadHelpers.Database.TransactionManager.StartOpenCloseTransaction())
  31.                 {
  32.                     var id = selectionResult.Value.GetObjectIds()[0];
  33.                     if (tr.GetObject(id, OpenMode.ForRead) is BlockReference blockReference &&
  34.                         ExtendedDataHelpers.IsApplicable(blockReference))
  35.                     {
  36.                         menuEnable = true;
  37.                     }    
  38.                     tr.Commit();
  39.                 }
  40.                
  41.             }
  42.  
  43.             contextMenuExtension.MenuItems[0].Visible = menuEnable;
  44.             contextMenuExtension.MenuItems[1].Visible = menuEnable;
  45.             contextMenuExtension.MenuItems[2].Visible = menuEnable;
  46.         }
  47.         catch
  48.         {
  49.             // ignore
  50.         }
  51.     }
  52. }
  53.  
  54. private static void CreateAnalogMenuItem_Click(object sender, EventArgs e)
  55. {
  56.     AcApp.DocumentManager.MdiActiveDocument.SendStringToExecute(
  57.         "_.mpESKDCreateAnalog ", false, false, false);
  58.  
  59. }
  60.  
  61. private static void DetachCreateAnalogContextMenu()
  62. {
  63.     if (_intellectualEntityContextMenu != null)
  64.     {
  65.         var rxObject = RXObject.GetClass(typeof(BlockReference));
  66.         Autodesk.AutoCAD.ApplicationServices.Application.RemoveObjectContextMenuExtension(rxObject, _intellectualEntityContextMenu);
  67.         _intellectualEntityContextMenu = null;
  68.     }
  69. }

Я добавил пару пунктов в меню для наглядности, хотя проблема наблюдается и с одним пунктом.
Итак, что имеем: при вызове контекстного меню для блока, который подходит по условиям, все хорошо:



А вот при вызове контекстного меню для другого блока наблюдаем следующее:



Т.е. ContextMenuExtension имеется и автокад его разделяет полосками (разделителями). А так как пункты меню скрыты, то остаются только разделители.

Есть идеи как убрать этот неприятный эффект?

Отмечено как Решение Александр Ривилис 28-11-2018, 15:29:39

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Коллега подсказал одну идею:
Я подписываюсь на событие ImpliedSelectionChanged и уже в нём делаю нужные проверки и включаю/отключаю меню (соответственно, убрав обработку PopUp):
Код - C# [Выбрать]
  1. private void Document_ImpliedSelectionChanged(object sender, EventArgs e)
  2. {
  3.     PromptSelectionResult psr = AcadHelpers.Editor.SelectImplied();
  4.     bool detach = true;
  5.     if (psr.Value != null && psr.Value.Count == 1)
  6.     {
  7.         using (AcadHelpers.Document.LockDocument())
  8.         {
  9.             using (OpenCloseTransaction tr = new OpenCloseTransaction())
  10.             {
  11.                 foreach (SelectedObject selectedObject in psr.Value)
  12.                 {
  13.                     if (selectedObject.ObjectId == ObjectId.Null)
  14.                         continue;
  15.                     var obj = tr.GetObject(selectedObject.ObjectId, OpenMode.ForRead);
  16.                     if (obj is BlockReference blockReference &&
  17.                         ExtendedDataHelpers.IsApplicable(blockReference))
  18.                     {
  19.                         AttachCreateAnalogContextMenu();
  20.                         detach = false;
  21.                     }
  22.                 }
  23.  
  24.                 tr.Commit();
  25.             }
  26.         }
  27.     }
  28.     if (detach)
  29.         DetachCreateAnalogContextMenu();
  30. }

Правда, ввиду того, что автокад может отписаться от события из-за какой-то ошибки в другом месте, мне этот вариант не очень импонирует

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Я подписываюсь на событие ImpliedSelectionChanged и уже в нём делаю нужные проверки и включаю/отключаю меню (соответственно, убрав обработку PopUp):
И это самое правильное в данном случае решение. А всё из-за того, что у тебя не нормальный Custom Entity.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
А всё из-за того, что у тебя не нормальный Custom Entity.
Этот вопрос не относится к Custom Entity в принципе )) У меня еще есть подобные места в других плагинах с подобным принципом - там тоже проверка на наличие XData. Да и в принципе таких вариантов можно придумать много. Например, мне нужен пункт меню у текста, если он красный)) Да мало-ли чего еще подобного

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 737
Коллега подсказал одну идею:
Я подписываюсь на событие ImpliedSelectionChanged и уже в нём делаю нужные проверки и включаю/отключаю меню
Сейчас посмотрел один свой плагин, где использую контекстное меню для определённых объектов - я там сделал точно так же.
Единственное отличие от представленного кода - когда я удаляю свой пункт из контекстного меню, я не уничтожаю его (код в первом посте, строка 67). Он остаётся висеть в памяти имея ссылку на статическую переменную. Соответственно, когда его надо будет снова добавить, мне не приходится создавать его заново (код в первом посте, строки 5-12). Вряд ли это сильно влияет на производительность, но мне так больше нравится.

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Дмитрий Загорулькин, спасибо за хорошее замечание. Я даже не обратил на это внимания )