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

ADN Club => AutoCAD .NET API => Тема начата: Александр Пекшев aka Modis от 26-05-2017, 18:08:36

Название: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Пекшев aka Modis от 26-05-2017, 18:08:36
Все - сдаюсь! Уже очень много испробовал вариантов - и логичных и нелогичных. Не могу никак понять правильный порядок действий (чувствую, что вот где-то рядом)
Имеется анонимный блок. Мне нужно изменять содержимое BlockTableRecord для вхождения блока. Т.е. получается всегда один BlockTableRecord и для него один BlockReference.
Для "описания" содержимого блока используется специальный класс, в котором я запоминаю ObjectId вхождения блока.
Для обновления содержимого получаются такие действия:
1. Я получаю экземпляр моего описательного класса по выбранному блоку
2. Я вызываю метод, которые обновляет геометрию (для примера - полилинию)
3. Затем методом из того-же класса я обновляю и BlockTableRecord:
     3.1. По сохраненному ObjectId получаю BlockReference. По нему получаю BlockTableRecord в котором все стирается
     3.2. В BlockTableRecord добавляется новое содержимое
4. Я вызываю метод UpdateAnonymousBlocks() для BlockTableRecord что вызывает и обновление BlockReference.
Так вот вся загвоздка в том, что для пункта 3 нужно открывать транзакцию!
При использовании этих шагов в GripsOverrule все отлично работает и никаких проблем нет. Но мне нужно, чтобы блок обновлялся и при изменении его свойств. А тут остается только ObjectOverrule.Close(). Но при всех возможных вариантах что-то сделать в ObjectOverrule.Close() (который срабатывает для BlockReference) ловятся фатальные ошибки. Причем, на попытках открыть транзакцию в п.3. Что в общем-то логично, т.к. BlockReference у меня в этот момент уже открыт и я его пытаюсь открыть опять
В общем - я уже порядком запутался =(
Как мне кажется - для обновления из GripsOverrule мне нужно оставить п.3, а при обновлении из ObjectOverrule.Close() мне нужно как-то использовать п.3 без транзакций
Поправьте и подскажите, если я что не так понимаю. А то и поговорить-то не с кем об этом ))
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Пекшев aka Modis от 26-05-2017, 18:45:46
Причем самое интересное то, что у меня частично получается реализовать описанное. Изменение блока ручками работает. Изменение блока через палитру свойств или некоторые команды (масштабирование) работает. Но когда я беру два таких блока, перемещаю ручки в одну точку и потом меняю их за ручку (т.е. два блока одновременно), то ловится фатал. И ловится он именно в ObjectOverrule.Close(). Но почему - вообще ума не приложу
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Ривилис от 26-05-2017, 20:50:16
Так вот вся загвоздка в том, что для пункта 3 нужно открывать транзакцию!
Не нужно. Ты не умеешь работать без транзакции? http://adn-cis.org/sozdanie-polyline3d-bez-ispolzovaniya-tranzakczii.html
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Пекшев aka Modis от 26-05-2017, 21:14:22
Так вот вся загвоздка в том, что для пункта 3 нужно открывать транзакцию!
Не нужно. Ты не умеешь работать без транзакции? http://adn-cis.org/sozdanie-polyline3d-bez-ispolzovaniya-tranzakczii.html
Вот только не работает
Из тела метода ObjectOverrule.Close() пытаюсь получить BlockTableRecord для текущего BlockReference и сразу ловлю ошибку "Ссылка на объект не указывает на экземпляр объекта." на строчке:
Код - C# [Выбрать]
  1. var blockRecord = (BlockTableRecord)blkRef.BlockTableRecord.GetObject(OpenMode.ForWrite);
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Ривилис от 26-05-2017, 22:41:57
Из тела метода ObjectOverrule.Close() пытаюсь получить BlockTableRecord для текущего BlockReference и сразу ловлю ошибку "Ссылка на объект не указывает на экземпляр объекта." на строчке:
Разницу между ObjectId.GetObject и ObjectId.Open чувствуешь? Статью прочитал внимательно?
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Пекшев aka Modis от 26-05-2017, 22:58:40
Из тела метода ObjectOverrule.Close() пытаюсь получить BlockTableRecord для текущего BlockReference и сразу ловлю ошибку "Ссылка на объект не указывает на экземпляр объекта." на строчке:
Разницу между ObjectId.GetObject и ObjectId.Open чувствуешь? Статью прочитал внимательно?
Оп-ля - невнимательно! К вечеру внимание рассеялось  ::)
Завтра буду пробовать
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Пекшев aka Modis от 27-05-2017, 18:23:29
Не, вариант с Open никак не прокатывает. Как и вариант ObjectId.GetObject.
Вот что у меня имеется:
Код - C# [Выбрать]
  1. private BlockTableRecord _blockRecord;
  2. public BlockTableRecord BlockRecord
  3. {
  4.     get
  5.     {
  6.         try
  7.         {
  8.             if (!BlockId.IsNull)
  9.             {
  10.                 using (var tr = AcadHelpers.Database.TransactionManager.StartTransaction())
  11.                 {
  12.                     var blkRef = (BlockReference)tr.GetObject(BlockId, OpenMode.ForWrite);
  13.                     _blockRecord = (BlockTableRecord)tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForWrite);
  14.                     _blockRecord.BlockScaling = BlockScaling.Uniform;
  15.                     if (_blockRecord.GetBlockReferenceIds(true, true).Count <= 1)
  16.                     {
  17.                         foreach (var objectId in _blockRecord)
  18.                         {
  19.                             objectId.GetObject(OpenMode.ForWrite).Erase(true);
  20.                         }
  21.                     }
  22.                     else
  23.                     {
  24.                         var blockTable = (BlockTable)tr.GetObject(AcadHelpers.Database.BlockTableId, OpenMode.ForWrite);
  25.                         _blockRecord = new BlockTableRecord { Name = "*U" };
  26.                         if (Annotative) _blockRecord.Annotative = AnnotativeStates.True;
  27.                         blockTable.Add(_blockRecord);
  28.                         tr.AddNewlyCreatedDBObject(_blockRecord, true);
  29.                         blkRef.BlockTableRecord = _blockRecord.Id;
  30.                     }
  31.                     tr.Commit();
  32.                 }
  33.                 using (var tr = AcadHelpers.Database.TransactionManager.StartTransaction())
  34.                 {
  35.                     var blkRef = (BlockReference)tr.GetObject(BlockId, OpenMode.ForWrite);
  36.                     _blockRecord = (BlockTableRecord)tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForWrite);
  37.                     var matrix3D = Matrix3d.Displacement(-InsertionPoint.GetAsVector()) * GetCurrentMatrixForBlockUpdate();
  38.                     foreach (var entity in Entities)
  39.                     {
  40.                         var transformedCopy = entity.GetTransformedCopy(matrix3D);
  41.                         _blockRecord.AppendEntity(transformedCopy);
  42.                         tr.AddNewlyCreatedDBObject(transformedCopy, true);
  43.                     }
  44.                     tr.Commit();
  45.                 }
  46.             }
  47.             else
  48.             {
  49.                 var matrix3D = Matrix3d.Displacement(-InsertionPoint.GetAsVector()) * GetCurrentMatrixForBlockUpdate();
  50.                 foreach (var ent in Entities)
  51.                 {
  52.                     var transformedCopy = ent.GetTransformedCopy(matrix3D);
  53.                     _blockRecord.AppendEntity(transformedCopy);
  54.                 }
  55.             }
  56.             return _blockRecord;
  57.         }
  58.  
  59.         catch (Exception exception)
  60.         {
  61.             MpExWin.Show(exception);
  62.             return null;
  63.         }
  64.     }
  65.     set => _blockRecord = value;
  66. }

Вариант с транзакцией в общем-то работает за исключением некоторых моментов. И самый гадкий из них, то, что метод ObjectOverrule.Close срабатывает и при копировании в буфер обмена, что вызывает новые ошибки
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Пекшев aka Modis от 29-05-2017, 18:48:20
то, что метод ObjectOverrule.Close срабатывает и при копировании в буфер обмена, что вызывает новые ошибки
Есть ли способы в ObjectOverrule определить, что происходит копирование в буфер? Или только обработка событий начала и окончания функции помогут?
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Ривилис от 29-05-2017, 19:08:11
Есть ли способы в ObjectOverrule определить, что происходит копирование в буфер? Или только обработка событий начала и окончания функции помогут?
В AutoCAD .NET API специального события для этого нет. Можно попробовать использовать Win32 API для этого. Пример здесь: https://stackoverflow.com/questions/2226920/how-do-i-monitor-clipboard-content-changes-in-c
Но проще я думаю отслеживать события начала и окончания команд.
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Александр Пекшев aka Modis от 29-05-2017, 20:57:13
Решил свою проблему неожиданным для меня способом! )) Сейчас тестирую, но на первый взгляд работает. Конечно, после большего количества тестов что-нибудь выплывет, но буду решать проблемы по мере поступления.
В общем - в работе метода ObjectOverrule.Close() у меня есть проверка:
Код - C# [Выбрать]
  1. if (dbObject != null &&
  2.     dbObject.IsModified &
  3.     !dbObject.IsUndoing &
  4.     !dbObject.IsErased &
  5.     !dbObject.IsEraseStatusToggled &
  6.     !dbObject.IsModifiedXData
  7. )
И если все подходит - тогда происходит "перерисовка" блока
Так вот я в эту проверку добавил !dbObject.IsNewObject. Теперь при изменении блока копированием, перемещением, из палитры свойств и т.п. блок перерисовывается. И не возникает проблем с копированием блока в буфер обмена

UPD: Сейчас, перечитывая сообщение, подумал, что логично будет убрать !dbObject.IsUndoing
Название: Re: ObjectOverrule.Close: Обновить BlockTableRecord для BlockReference
Отправлено: Дмитрий Загорулькин от 04-02-2019, 13:57:14
UPD: Сейчас, перечитывая сообщение, подумал, что логично будет убрать !dbObject.IsUndoing
Тогда, возможно, стоит убрать и !dbObject.IsEraseStatusToggled