Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?

Автор Тема: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?  (Прочитано 19336 раз)

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

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Здравствуйте!
Есть такой метод, создал специально для демонстрации проблемы:
Код - C# [Выбрать]
  1. [CommandMethod("TestSelectionAndSetImplied", CommandFlags.UsePickSet | CommandFlags.Redraw)]
  2.         public void Cmd5()
  3.         {
  4.             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  5.             PromptSelectionResult selRes = ed.GetSelection();
  6.             if (selRes.Status != PromptStatus.OK)
  7.                 ed.WriteMessage("\nНичего не выбрано!");
  8.             else
  9.             {
  10.                 ed.WriteMessage("\nВыбрано объектов: {0} шт.", selRes.Value.Count);
  11.                 ed.SetImpliedSelection(selRes.Value);
  12.             }
  13.         }
Суть работы такая - выбираем подходящие объекты и подсвечиваем их. Если предварительного выбора нет - то все в порядке. Но если есть предварительный выбор, то после завершения работы команды получается странная ситуация. Объекты остаются выбранными (это можно проверить, открыв окно свойств), но ручки у них не показывается, подсветки объектов нет. Стоит только щелкнуть мышкой по чертежу - подсветка сразу появляется. Метод редактора UpdateScreen не помогает. Комбинацию флагов уже пробовал самую разную, оставил как должно быть по-правильному. Пробовал перед ed.SetImpliedSelection(selRes.Value) выполнить обнуление предвыбора таким способом: ed.SetImpliedSelection(new ObjectId[0]) - тоже не помогает. Идей больше не осталось.
Что можно сделать с этим, чтобы подсветка не пропадала?
« Последнее редактирование: 25-06-2014, 21:05:10 от Александр Ривилис »

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

  • Administrator
  • *****
  • Сообщений: 13832
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Я не тестировал еще, но судя по http://adndevblog.typepad.com/autocad/2012/05/clear-pick-first-selection-set-.html не хватает флага CommandFlags.Modal.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Добавил флаг, добавил обнуление предвыбора:
Код - C# [Выбрать]
  1.        [CommandMethod("TestSelectionAndSetImplied", CommandFlags.UsePickSet | CommandFlags.Redraw | CommandFlags.Modal)]
  2.         public void Cmd5()
  3.         {
  4.             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  5.             PromptSelectionResult selRes = ed.GetSelection();
  6.             if (selRes.Status != PromptStatus.OK)
  7.                 ed.WriteMessage("\nНичего не выбрано!");
  8.             else
  9.             {
  10.                 ed.WriteMessage("\nВыбрано объектов: {0} шт.", selRes.Value.Count);
  11.                 ed.SetImpliedSelection(new ObjectId[0]);
  12.                 ed.SetImpliedSelection(selRes.Value);
  13.             }
  14.         }
К сожалению, не помогло :(.
« Последнее редактирование: 25-06-2014, 21:05:38 от Александр Ривилис »

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Новые наблюдения. Если в чертеже один раз прогнать эту команду с предварительным выбором, то даже изначально хорошо работающий вариант без предвыбора начинает вести себя так же, как и вариант с предвыбором. Получается, что либо код некорректный и портит работу автокада, либо это баг.
Еще одна особенность всплыла. Если переделать код таким образом:
Код - C# [Выбрать]
  1. [CommandMethod("TestSelectionAndSetImplied", CommandFlags.UsePickSet | CommandFlags.Redraw | CommandFlags.Modal)]
  2.         public void Cmd5()
  3.         {
  4.             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  5.             PromptSelectionResult selRes = ed.GetSelection();
  6.             if (selRes.Status != PromptStatus.OK)
  7.                 ed.WriteMessage("\nНичего не выбрано!");
  8.             else
  9.             {
  10.                 // Перемещаем задание предвыбора повыше
  11.                 ed.SetImpliedSelection(new ObjectId[0]);
  12.                 ed.SetImpliedSelection(selRes.Value);
  13.                 ed.WriteMessage("\nВыбрано объектов: {0} шт.", selRes.Value.Count);
  14.                 // После задания предвыбора попытаемся пройтись по объектам набора
  15.                 foreach (ObjectId entId in selRes.Value.GetObjectIds())
  16.                 {
  17.                 }
  18.             }
  19.         }
То, при попытке итерации по набору, будет возникать исключение. Получается, что метод SetImpliedSelection портит передаваемый в него набор?
« Последнее редактирование: 25-06-2014, 21:05:56 от Александр Ривилис »

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

  • Administrator
  • *****
  • Сообщений: 13832
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Сравни со своим кодом:
Код - C# [Выбрать]
  1.     [CommandMethod("TestSelectionAndSetImplied",
  2.        CommandFlags.UsePickSet | CommandFlags.Redraw | CommandFlags.Modal)]
  3.     public void TestSelectionAndSetImplied()
  4.     {
  5.       Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  6.       // Проверяем был ли предварительный выбор
  7.       PromptSelectionResult selRes = ed.SelectImplied();
  8.       // Если не было - просим пользователя выбрать
  9.       if (selRes.Status != PromptStatus.OK) {
  10.         selRes = ed.GetSelection();
  11.       }
  12.       if (selRes.Status != PromptStatus.OK)
  13.         ed.WriteMessage("\nНичего не выбрано!");
  14.       else {
  15.         ed.WriteMessage("\nВыбрано объектов: {0} шт.", selRes.Value.Count);
  16.         ed.SetImpliedSelection(selRes.Value.GetObjectIds());
  17.       }
  18.     }
Считать баг ли то что ты нашел или нет - достаточно сложно. Хотя видно, что твоя последовательность вызова приводит AutoCAD в тупик.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Предлагаете использовать метод SelectImplied? Выход, конечно, но у него нет никакого фильтра - придется дополнительно делать обработку предвыбора, потом дублировать ее в фильтре для GetSelection... Что же, попробую так сделать, спасибо!

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

  • Administrator
  • *****
  • Сообщений: 13832
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
В любом случае это лучше чем ничего. Кстати в чистом ObjectARX с этим проблем нет: Выбор определенных примитивов из набора предварительного выбора
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Да в NET с обработкой предвыбора с помощью SelectImplied тоже проблем нет. Ведь, как я понял, в статье идет разбор работы с его аналогом в ObjectARX?
В общем, удалось получить то что нужно с помощью SelectImplied, даже получилось вынести фильтрацию в отдельный метод, избежав дублирования кода. Так что, моя проблема решилась, спасибо!

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

  • Administrator
  • *****
  • Сообщений: 13832
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Ведь, как я понял, в статье идет разбор работы с его аналогом в ObjectARX?
Не совсем так. В ObjectARX функция acedSSGet(L"_I",...); позволяет сразу отфильтровать из набора предварительного выбора (например, только полилиниии).
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Да, точно! Лисповская SSGET функционал наверняка берет из ObjectARX'овской acedSSGet. В ней тоже есть этот флаг "I" и можно фильтр задать. В статье это не показано, поэтому я не понял Вас.

Отмечено как Решение Александр Ривилис 05-10-2014, 03:10:49

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Может кому пригодится. Причесал маленько.
P.S. Обновлено с учетом замечаний:
Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3.  
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.DatabaseServices;
  6. using Autodesk.AutoCAD.ApplicationServices;
  7. using Autodesk.AutoCAD.EditorInput;
  8.  
  9. namespace CustomCADSupport
  10. {
  11.     public class CustomSelectionClass
  12.     {
  13.         [CommandMethod("TestSelectPlines", CommandFlags.UsePickSet | CommandFlags.Redraw | CommandFlags.Modal)]
  14.         public void SelectPlinesRun()
  15.         {
  16.             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  17.             PromptSelectionOptions selOpt = new PromptSelectionOptions();
  18.             selOpt.MessageForAdding = "\nВыберите полилинии: ";
  19.             // Производим выбор объектов собственным методом
  20.             PromptSelectionResult selRes = ed.SelectObjectsWithFilter(selOpt, ObjectIsPline);
  21.             if (selRes.Status != PromptStatus.OK)
  22.                 ed.WriteMessage("\nНичего не выбрано!");
  23.             else
  24.             {
  25.                 ed.WriteMessage("\nВыбрано полилиний: {0} шт.", selRes.Value.Count);
  26.                 ed.SetImpliedSelection(selRes.Value.GetObjectIds());
  27.             }
  28.         }
  29.  
  30.         /// <summary>
  31.         /// Метод проверки, является ли объект полилинией
  32.         /// </summary>        
  33.         /// <param name="objId">Id объекта</param>
  34.         /// <returns>True - объект является полилинией, false - не является</returns>
  35.         bool ObjectIsPline(ObjectId objId)
  36.         {
  37.             bool retVal;
  38.             Database db = objId.Database;
  39.             using (Transaction tr = db.TransactionManager.StartTransaction())
  40.             {
  41.                 DBObject obj = tr.GetObject(objId, OpenMode.ForRead);
  42.                 retVal = obj is Polyline || obj is Polyline2d || obj is Polyline3d;
  43.                 tr.Commit();
  44.             }
  45.             return retVal;
  46.         }
  47.     }
  48.  
  49.     public static class SelectionMethods
  50.     {
  51.         /// <summary>
  52.         /// переменная для передачи метода проверки в метод отклика на выбор объектов
  53.         /// </summary>        
  54.         static Func<ObjectId, bool> _filterObjectMethod;
  55.  
  56.         /// <summary>
  57.         /// Выбор объектов на чертеже с учетом предварительного выбора и с использованием фильтра
  58.         /// </summary>
  59.         /// <param name="ed">Редактор документа</param>
  60.         /// <param name="selOpt">Опции выбора</param>
  61.         /// <param name="filterObjectMethod">
  62.         /// Метод фильтрации.
  63.         /// Принимает один аргумент - ID объекта чертежа.
  64.         /// Возвращает true если объект прошел проверку и false - если не прошел
  65.         /// </param>
  66.         /// <returns>Результат выбора - объект PromptSelectionResult</returns>
  67.         public static PromptSelectionResult SelectObjectsWithFilter(this Editor ed, PromptSelectionOptions selOpt, Func<ObjectId, bool> filterObjectMethod)
  68.         {
  69.             _filterObjectMethod = filterObjectMethod;
  70.             Database db = ed.Document.Database;
  71.  
  72.             // Обработка предварительного выбора
  73.             PromptSelectionResult selRes = ed.SelectImplied();
  74.             List<ObjectId> trueObjIds = new List<ObjectId>();
  75.             if (selRes.Status == PromptStatus.OK)
  76.             {
  77.                 foreach (ObjectId objId in selRes.Value.GetObjectIds())
  78.                 {
  79.                     if (_filterObjectMethod(objId)) trueObjIds.Add(objId);
  80.                 }
  81.             }
  82.  
  83.             if (trueObjIds.Count > 0)
  84.             {
  85.                 ed.SetImpliedSelection(trueObjIds.ToArray());
  86.                 selRes = ed.SelectImplied();
  87.                 // Обнуляем предвыбор
  88.                 ed.SetImpliedSelection(new ObjectId[0]);
  89.             }
  90.             else
  91.             {
  92.                 SelectionAddedEventHandler selHandler = new SelectionAddedEventHandler(ed_SelectionAdded);
  93.                 ed.SelectionAdded += selHandler;
  94.                 selRes = ed.GetSelection(selOpt);
  95.                 ed.SelectionAdded -= selHandler;
  96.             }
  97.             return selRes;
  98.         }
  99.  
  100.         /// <summary>
  101.         /// Метод фильтрации выбираемых с помощью GetSelection объектов в режиме реального времени
  102.         /// </summary>
  103.         /// <param name="sender"></param>
  104.         /// <param name="e"></param>
  105.         static void ed_SelectionAdded(object sender, SelectionAddedEventArgs e)
  106.         {            
  107.             ObjectId[] selIds = e.AddedObjects.GetObjectIds();            
  108.             for (int i = 0; i < selIds.Length; i++)
  109.             {
  110.                 if (!_filterObjectMethod(selIds[i])) e.Remove(i);
  111.             }
  112.         }
  113.     }
  114. }
  115.  
« Последнее редактирование: 27-12-2013, 20:56:11 от Загорулькин Дмитрий »

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

  • Administrator
  • *****
  • Сообщений: 13832
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Пара замечаний по поводу функции bool ObjectIsPline(Database db, ObjectId objId)
1. Параметр Database лишний, так как его можно получить из objId: objId.Database
2. В транзакции и открытии примитива нет необходимости - имя класса примитива можно получить из ObjectId используя свойство objId.ObjectClass
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Спасибо! Вроде не первый день пишу на NET под автокад и не знал, что ID хранит ссылку на базу данных  :o
По поводу транзакции - да, в этом примере нет необходимости. У меня же в программе производится выбор объектов Civil 3D, которые являются "дочерними" по отношению к другим объектам. И без транзакции к "родителям" не добраться. Ели же просто нужно отфильтровать объекты по типам - да, транзакция будет лишней. Ну, думаю, что кому надо будет, сам решит как сделать, посмотрев пример и Ваше замечание.

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

  • Administrator
  • *****
  • Сообщений: 13832
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
И без транзакции к "родителям" не добраться.
Ну почему же. Если ты обратил внимание, то метод ObjectClass возвращает экземпляр класса RXClass, у которого есть метод RXClass.IsDerivedFrom - т.е. определить наследник ли он в иерархии классов или нет очень просто.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Да там нет наследования классов, поэтому я взял в кавычки слова. Так и знал, что введу в заблуждение :) Просто один объект(вспомогательный) служит для отображения другого(основного) на другом виде.