Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Дмитрий Загорулькин от 25-12-2013, 16:00:27

Название: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 25-12-2013, 16:00:27
Здравствуйте!
Есть такой метод, создал специально для демонстрации проблемы:
Код - 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]) - тоже не помогает. Идей больше не осталось.
Что можно сделать с этим, чтобы подсветка не пропадала?
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 25-12-2013, 16:16:07
Я не тестировал еще, но судя по http://adndevblog.typepad.com/autocad/2012/05/clear-pick-first-selection-set-.html не хватает флага CommandFlags.Modal.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 25-12-2013, 16:23:41
Добавил флаг, добавил обнуление предвыбора:
Код - 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.         }
К сожалению, не помогло :(.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 25-12-2013, 18:57:33
Новые наблюдения. Если в чертеже один раз прогнать эту команду с предварительным выбором, то даже изначально хорошо работающий вариант без предвыбора начинает вести себя так же, как и вариант с предвыбором. Получается, что либо код некорректный и портит работу автокада, либо это баг.
Еще одна особенность всплыла. Если переделать код таким образом:
Код - 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 портит передаваемый в него набор?
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 25-12-2013, 19:13:56
Сравни со своим кодом:
Код - 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 в тупик.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 25-12-2013, 21:27:22
Предлагаете использовать метод SelectImplied? Выход, конечно, но у него нет никакого фильтра - придется дополнительно делать обработку предвыбора, потом дублировать ее в фильтре для GetSelection... Что же, попробую так сделать, спасибо!
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 25-12-2013, 21:59:07
В любом случае это лучше чем ничего. Кстати в чистом ObjectARX с этим проблем нет: Выбор определенных примитивов из набора предварительного выбора (http://adn-cis.org/vyibor-opredelennyix-primitivov-iz-nabora-predvaritelnogo-vyibora.html)
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 26-12-2013, 11:57:21
Да в NET с обработкой предвыбора с помощью SelectImplied тоже проблем нет. Ведь, как я понял, в статье идет разбор работы с его аналогом в ObjectARX?
В общем, удалось получить то что нужно с помощью SelectImplied, даже получилось вынести фильтрацию в отдельный метод, избежав дублирования кода. Так что, моя проблема решилась, спасибо!
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 26-12-2013, 12:00:37
Ведь, как я понял, в статье идет разбор работы с его аналогом в ObjectARX?
Не совсем так. В ObjectARX функция acedSSGet(L"_I",...); позволяет сразу отфильтровать из набора предварительного выбора (например, только полилиниии).
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 26-12-2013, 13:11:28
Да, точно! Лисповская SSGET функционал наверняка берет из ObjectARX'овской acedSSGet. В ней тоже есть этот флаг "I" и можно фильтр задать. В статье это не показано, поэтому я не понял Вас.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 27-12-2013, 13:00:39
Может кому пригодится. Причесал маленько.
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.  
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 27-12-2013, 15:26:43
Пара замечаний по поводу функции bool ObjectIsPline(Database db, ObjectId objId)
1. Параметр Database лишний, так как его можно получить из objId: objId.Database
2. В транзакции и открытии примитива нет необходимости - имя класса примитива можно получить из ObjectId используя свойство objId.ObjectClass
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 27-12-2013, 20:50:33
Спасибо! Вроде не первый день пишу на NET под автокад и не знал, что ID хранит ссылку на базу данных  :o
По поводу транзакции - да, в этом примере нет необходимости. У меня же в программе производится выбор объектов Civil 3D, которые являются "дочерними" по отношению к другим объектам. И без транзакции к "родителям" не добраться. Ели же просто нужно отфильтровать объекты по типам - да, транзакция будет лишней. Ну, думаю, что кому надо будет, сам решит как сделать, посмотрев пример и Ваше замечание.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 27-12-2013, 20:56:49
И без транзакции к "родителям" не добраться.
Ну почему же. Если ты обратил внимание, то метод ObjectClass возвращает экземпляр класса RXClass, у которого есть метод RXClass.IsDerivedFrom - т.е. определить наследник ли он в иерархии классов или нет очень просто.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 27-12-2013, 21:23:47
Да там нет наследования классов, поэтому я взял в кавычки слова. Так и знал, что введу в заблуждение :) Просто один объект(вспомогательный) служит для отображения другого(основного) на другом виде.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 27-12-2013, 22:05:29
Так и знал, что введу в заблуждение :)
Таки смог!  :)
Вроде не первый день пишу на NET под автокад и не знал, что ID хранит ссылку на базу данных  :o
Если быть точным, то ObjectId хранит ссылку на базу данных только (!!!) если он уже добавлен в базу данных. В противном случае ObjectId.Database вернет null, что впрочем понятно. В данном случае очевидно, что все примитивы, которые мы собираемся фильтровать уже находятся в базе. Более того, конкретно в базе Application.DocumentManager.MdiActiveDocument.Database
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Андрей Бушман от 27-12-2013, 22:06:12
Может кому пригодится. Причесал маленько.
Нечто похожее я размещал здесь (http://bushman-andrey.blogspot.ru/2013/12/autocad-linq.html) (см. код класса DatabaseExtensionMethods.cs). Там даны несколько вариантов выборки, группировки, модификации и генерации произвольных данных.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 28-12-2013, 01:38:15
Андрей. Я что-то не уловил зачем функция public static Db.ObjectId GetCurrentSpaceObjectId(this Db.Database database) и чем она отличается от обычного Database.CurrentSpaceId
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Андрей Бушман от 28-12-2013, 09:37:29
Андрей. Я что-то не уловил зачем функция public static Db.ObjectId GetCurrentSpaceObjectId(this Db.Database database) и чем она отличается от обычного Database.CurrentSpaceId
Если в данный момент вы находитесь в видовом экране, размещённом на листе, то чей идентификатор покажет Database.CurrentSpaceId: Листа или Модели? Насколько я помню, он покажет идентификатор листа, на котором находится активный видовой экран. На всякий случай перепроверьте - я не могу, т. к. Linux.

Мне нужно, чтобы в этом случае возвращался идентификатор модели, поскольку в данный момент работа происходит именно в этом пространстве.

Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 30-12-2013, 01:56:11
Код для проверки:
Код - C# [Выбрать]
  1. [CommandMethod("TestSpace", CommandFlags.Modal)]
  2. static public void TestSpace()
  3. {
  4.   Document doc = Application.DocumentManager.MdiActiveDocument;
  5.   Editor ed = doc.Editor;
  6.   Database db = doc.Database;
  7.   ObjectId curSpaceId = db.CurrentSpaceId;
  8.   using (BlockTableRecord btr = curSpaceId.Open(OpenMode.ForRead) as BlockTableRecord) {
  9.     ed.WriteMessage("\nName of space: {0}", btr.Name);
  10.   }
  11. }
В пространстве модели:
Name of space: *Model_SpaceВ пространстве листа:
Name of space: *Paper_SpaceВ видовом экране внутри пространства листа:
Name of space: *Model_SpaceТ.е., как я понимаю именно то, что тебе было нужно...
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Андрей Бушман от 30-12-2013, 10:54:39
Согласен, велосипед! Исправил.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Phil от 27-02-2018, 13:19:08
Добрый день!

В процессе разработки своей программы, нашел данную ветку форума. Решение, приведенное здесь, мне здорово помогло.
Но есть одна проблема: в результате выполнения приведенного кода по обработке предварительного набора объектов с фильтрацией полилиний, отфильтрованные объекты не только подсвечиваются, но и появляются "ручки". Как сделать, что бы ручки не появлялись? Мне нужно продолжить команду, и появление ручек в этот момент совсем некстати.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Дмитрий Загорулькин от 27-02-2018, 14:03:53
Как сделать, что бы ручки не появлялись?
В 26 строке удалить:
Код - C# [Выбрать]
  1. ed.SetImpliedSelection(selRes.Value.GetObjectIds());
И добавить обработку выбранных объектов методом Entity.Highlight()
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 27-02-2018, 14:47:26
в результате выполнения приведенного кода по обработке предварительного набора объектов с фильтрацией полилиний, отфильтрованные объекты не только подсвечиваются, но и появляются "ручки".
Если имеется в виду под подсветкой выбор объектов, то ручки будут всегда. А если нет и нужно просто показать объекты, то Дмитрий Загорулькин прав.
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Phil от 27-02-2018, 14:51:55
И добавить обработку выбранных объектов методом Entity.Highlight()
Если имеется в виду под подсветкой выбор объектов, то ручки будут всегда. А если нет и нужно просто показать объекты, то Дмитрий Загорулькин прав.
Благодарю! Нужна именно подсветка на период выполнения моей команды. А по завершении команды, я очевидно, должен вручную снять подсветку, иначе она останется, верно? Методом Entity.UnHighlight() ?
Название: Re: Метод GetSelection и предварительный выбор - некорректная работа. Как побороть?
Отправлено: Александр Ривилис от 27-02-2018, 14:57:23
А по завершении команды, я очевидно, должен вручную снять подсветку, иначе она останется, верно? Методом Entity.UnHighlight() ?
Да.