Проблемы при копировании таблиц методом WblockCloneObjects

Автор Тема: Проблемы при копировании таблиц методом WblockCloneObjects  (Прочитано 24774 раз)

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

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

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

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Всем привет.
Для копирования таблицы из другого файла использую метод WblockCloneObjects. Вот примерно так это выглядит:
Код - C# [Выбрать]
  1. var tableStyle = "bla_bla_bla";
  2. // Имя файла из которого берем таблицу
  3. var sourceFileName = Path.Combine(dir, "Tables.dwg");
  4. // Read the DWG into a side database
  5. sourceDb.ReadDwgFile(sourceFileName, FileShare.Read, true, string.Empty);
  6. var tblIds = new ObjectIdCollection();
  7. // Создаем пустую таблицу
  8. var tbl = new Table();
  9. using (var myT = sourceDb.TransactionManager.StartTransaction())
  10. {
  11.     var sourceBtr = (BlockTableRecord) myT.GetObject(sourceDb.CurrentSpaceId, OpenMode.ForRead, false);
  12.     foreach (var obj in sourceBtr)
  13.     {
  14.         var ent = (Entity)myT.GetObject(obj, OpenMode.ForRead);
  15.         if (ent is Table)
  16.         {
  17.             var tblsty = (Table)myT.GetObject(obj, OpenMode.ForRead);
  18.             if (tblsty.TableStyleName.Equals(tableStyle))
  19.             {
  20.                 tblIds.Add(tblsty.ObjectId);
  21.                 var im = new IdMapping();
  22.                 sourceDb.WblockCloneObjects(tblIds, db.CurrentSpaceId, im, DuplicateRecordCloning.Replace, false);
  23.                 tbl = (Table) tr.GetObject(im.Lookup(tblsty.ObjectId).Value, OpenMode.ForWrite);
  24.                 break;
  25.             }
  26.         }
  27.     }
  28.     myT.Commit();
  29. }
Далее по коду эту таблицу перемещаю в указанную пользователем точку. В общем ничего сверхъестественного в этом коде нет на первый взгляд, однако есть очень неприятная проблема - этот вариант переопределяет текстовый стиль!
Допустим в файле из, которого я копирую, есть текстовый стиль Standard у которого шрифт стоит Arial и степень сжатия 1.0. И этот текстовый стиль применен к таблице. В моем текущем документе тоже есть текстовый стиль Standard, но с другим шрифтом и степенью сжатия (например Simplex.shx и 0.7). Так вот - после работы выше написанного кода текстовый стиль в текущем документе тоже переопределится (шрифт станет Arial, степень сжатия 1.0).
Предположил, что дело в самом методе WblockCloneObjects и попробовал заменить DuplicateRecordCloning.Replace на DuplicateRecordCloning.Ignore. И вроде все сработало (т.е. текстовый стиль не переопределился, а таблица скопировалась), но при попытке дальнейших манипуляций с таблице автокад вылетает с FatalError
Как быть?
« Последнее редактирование: 26-05-2015, 12:53:23 от Александр Ривилис »

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
1) Не забывай про форматирование кода (смотри у меня в подписи)
2) Попробуй заменить DuplicateRecordCloning.Replace на DuplicateRecordCloning.MangleName
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

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

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

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
P.S.: Вообще-то в этом коде ты клонируешь не таблицу, а табличный стиль.
Разве?
Я "беру" таблицу: var tblsty = (Table)myT.GetObject(obj, OpenMode.ForRead);
проверяю, что у нее табличный стиль нужный мне: if (tblsty.TableStyleName.Equals(tableStyle))
и делаю "клон" по ObjectId таблицы: tblIds.Add(tblsty.ObjectId);
Просто имена переменных корявые

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

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

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Код - C# [Выбрать]
  1. var tbl = new Table();
  2. ...
  3. tbl = (Table) tr.GetObject(im.Lookup(tblsty.ObjectId).Value, OpenMode.ForWrite);
Зачем выполнять инициализацию через new, если ты всё равно не используешь значение, но выполняешь повторное присвоение значения? Вопрос риторический. Тогда уж лучше так:
Код - C# [Выбрать]
  1. var tbl = null;

Получать Entity для каждого ObjectId лишь для того, чтобы проверить тип - это затратно и нецелесообразно. Имхо лучше так:
Код - C# [Выбрать]
  1. // за рамками цикла
  2. var targetType = RXClass.GetClass(typeof(Entity));
  3. ..
  4. // в цикле
  5. if (id.ObjectClass.IsDerivedFrom(targetType)){
  6.  
  7. }

А это что за ужасы?
Код - C# [Выбрать]
  1. var ent = (Entity)myT.GetObject(obj, OpenMode.ForRead);
  2.         if (ent is Table)
  3.         {
  4.             var tblsty = (Table)myT.GetObject(obj, OpenMode.ForRead);
зачем тебе ещё и tblsty, если  ты уже получил искомое в ent? Эти две переменные у тебя указывают на один и тот же объект. Кроме того, если объект вдруг окажется не Entity то в первой строке кода ты получишь исключение.

А этой строчкой кода ты что хотел сказать?
Код - C# [Выбрать]
  1. tbl = (Table) tr.GetObject(im.Lookup(tblsty.ObjectId).Value, OpenMode.ForWrite);

Код - C# [Выбрать]
  1. if (tblsty.TableStyleName.Equals(tableStyle))
А если регистр символов не совпадёт? Вопрос риторический.

Цитировать
Просто имена переменных корявые
Корявые имена способствуют соответствующему восприятию, путанице и появлению ошибок.

Честно говоря, мне даже "больно" читать твой код... :(

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

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Так лучше?
Код - C# [Выбрать]
  1. var doc = AcApp.DocumentManager.MdiActiveDocument;
  2. var currentDb = doc.Database;
  3. var tableStyle = "bla_bla_bla";
  4. // Read the DWG into a side database
  5. sourceDb.ReadDwgFile(sourceFileName, FileShare.Read, true, string.Empty);
  6. var tblIds = new ObjectIdCollection();
  7. // Создаем пустую таблицу
  8. Table tbl = null;
  9.  
  10. using (var sourceTr = sourceDb.TransactionManager.StartTransaction())
  11. {
  12.     var sourceBtr = (BlockTableRecord)sourceTr.GetObject(sourceDb.CurrentSpaceId, OpenMode.ForRead, false);
  13.     foreach (var objectId in sourceBtr)
  14.     {
  15.         if (sourceTr.GetObject(objectId, OpenMode.ForRead).GetType() == typeof(Table))
  16.         {
  17.             var sourceTable = (Table)sourceTr.GetObject(objectId, OpenMode.ForRead);
  18.             if (sourceTable.TableStyleName.Equals(tableStyle)) // Если стиль таблицы совпадает - это искомая таблица
  19.             {
  20.                 tblIds.Add(sourceTable.ObjectId);
  21.                 var im = new IdMapping();
  22.                 sourceDb.WblockCloneObjects(tblIds, currentDb.CurrentSpaceId, im, DuplicateRecordCloning.Replace, false);
  23.                 tbl = (Table)tr.GetObject(im.Lookup(sourceTable.ObjectId).Value, OpenMode.ForWrite);
  24.                 break;
  25.             }
  26.         }
  27.     }
  28.     sourceTr.Commit();
  29. }
  30. sourceDb.Dispose();
  31. if(tbl == null) return;
  32.  
  33. // Перемещаем
  34. var entJig = new TableJig(tbl, pointAligin, tbl.Width, tbl.Height);
  35. var pr = ed.Drag(entJig);
  36. if (pr.Status != PromptStatus.OK) return;
  37. doc.TransactionManager.QueueForGraphicsFlush();
  38. // Дальше идут другие действия с таблицей...
Этот кода - самый, что ни есть, стандартный из всех возможных примеров, что можно найти. Вот например http://adndevblog.typepad.com/autocad/2012/09/copying-deepcloning-from-one-drawing-to-another-using-net.html или http://through-the-interface.typepad.com/through_the_interface/2006/08/import_blocks_f.html
Обычные шаги:
1. Открыл базу данных документа
2. Нашел подходящий объект
3. Скопировал в текущую базу данных
Естественно таблица копируется с табличным стилем. А он за собой тянет текстовый стиль, который в свою очередь перезаписывается в текущем документе (если есть такой-же)

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Ты пробовал сделать то, что тебе советовали (п.2)?

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Этот кода - самый, что ни есть, стандартный из всех возможных примеров, что можно найти.
Допущенные тобой ошибки - твои личные. По обозначенным ссылкам они отсутствуют, так что не нужно пенять на других.

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

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Ты пробовал сделать то, что тебе советовали (п.2)?
Да. Если выставить DuplicateRecordCloning.MangleName, то стили (табличный и текстовый) копируются в текущий документ с приставками к имени символов $0$. Конечно текстовый стиль не перезаписывается, но при каждом использовании кода текущие списки стилей будут, простите за мой французский, засераться новыми копиями стилей
Этот кода - самый, что ни есть, стандартный из всех возможных примеров, что можно найти.
Допущенные тобой ошибки - твои личные. По обозначенным ссылкам они отсутствуют, так что не нужно пенять на других.

Допущенные мной ошибки ни коем случаем не являются причиной вопроса, описанного в самом начале)))

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Попробуй вариант DuplicateRecordCloning.Ignore:
Цитировать
If a duplicate is found, ignore the clone, and continue to use the existing record in the destination database. This is how the INSERT command and Database.insert() operate.
Или DuplicateRecordCloning.UnmangleName:
Цитировать
Primarily used by RefEdit when it checks records back into the origin database. Any mangling done by MangleName is undone, and then it defaults to Ignore. In other words, if a duplicate is found after the mangling is removed, AutoCAD continues to use the existing record, and ignores the cloned one.
Их описание в справке не совсем ясно для меня, поэтому лучше проверить эти варианты на примере.

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

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Попробуй вариант DuplicateRecordCloning.Ignore:
Цитировать
If a duplicate is found, ignore the clone, and continue to use the existing record in the destination database. This is how the INSERT command and Database.insert() operate.
Или DuplicateRecordCloning.UnmangleName:
Цитировать
Primarily used by RefEdit when it checks records back into the origin database. Any mangling done by MangleName is undone, and then it defaults to Ignore. In other words, if a duplicate is found after the mangling is removed, AutoCAD continues to use the existing record, and ignores the cloned one.
Их описание в справке не совсем ясно для меня, поэтому лучше проверить эти варианты на примере.
Вот тут и начинается самое интересное)
Сразу говорю - да, я согласен заранее, что код может быть "печальным"
Дальше по коду при действиях с этой таблицей есть динамическая вставка строк.
Запускается так (этот код идет прям сразу после приведенного выше)
Код - C# [Выбрать]
  1. // Динамическая вставка строк
  2. if (this.ChkDynRowsStandard.IsChecked.Value)
  3. {
  4.     var jig = new TableAddCellsJig
  5.     {
  6.         FPt = tbl.Position,
  7.         RowH = double.Parse(this.TbRowHeight.Text) * Scale(this.CbScales.SelectedItem.ToString()),
  8.         StopRows = int.Parse(xmltbl.Attribute("DataRow").Value),
  9.         TbH = tbl.GeometricExtents.MaxPoint.Y - tbl.GeometricExtents.MinPoint.Y
  10.     };
  11.     jig.StartJig(tbl);
  12. }
Ну и как это работает:
Извините, вам запрещён просмотр содержимого спойлеров.

Дело в том, что в чистом чертеже все работает. Но если я пытаюсь запустить код в существующем чертеже, то автокад вылетает с FatalError. Причем вот именно в тот момент, когда пытается сработать Jig

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Если резюмировать, то у тебя есть такие варианты:
1) Использовать DuplicateRecordCloning.Replace и тогда заменяются стили в чертеже-приёмнике.
2) Использовать DuplicateRecordCloning.Ignore и тогда используются стили, которые есть в чертеже-приёмнике. Кстати, это не только текстовый стиль, но и табличный стиль. То, что у тебя при этом возникает Fatal Error мы пока рассматривать не будем.
3) Использовать DuplicateRecordCloning.MangleName и тогда вопросов нет - свои текстовый и табличные стили.
Так какой вариант тебе нужен?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Если резюмировать, то у тебя есть такие варианты:
1) Использовать DuplicateRecordCloning.Replace и тогда заменяются стили в чертеже-приёмнике.
2) Использовать DuplicateRecordCloning.Ignore и тогда используются стили, которые есть в чертеже-приёмнике. Кстати, это не только текстовый стиль, но и табличный стиль. То, что у тебя при этом возникает Fatal Error мы пока рассматривать не будем.
3) Использовать DuplicateRecordCloning.MangleName и тогда вопросов нет - свои текстовый и табличные стили.
Так какой вариант тебе нужен?
Естественно идеальный вариант п.2. Кстати DuplicateRecordCloning.UnmangleName с виду делает почти тоже самое, что и DuplicateRecordCloning.Ignore
Остается вопрос - почему Fatal Error?

Пытался отладкой найти место где именно случается ошибка, но у меня какие-то проблемы - при отладке автокад открывается вообще без текстовых стилей
Вот я и пошел на форум - возможно проблема очевидна, но я ее не вижу

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Попробуй заменить
Код - C# [Выбрать]
  1. sourceDb.WblockCloneObjects(tblIds, currentDb.CurrentSpaceId, im, DuplicateRecordCloning.Ignore, false);
на
Код - C# [Выбрать]
  1. currentDb.WblockCloneObjects(tblIds, currentDb.CurrentSpaceId, im, DuplicateRecordCloning.Ignore, false);
Если не поможет. Делай новый минимальный тестовый проект, на котором воспроизводится Fatal Error при Ignore и чертеж с таблицей. Отправлю в Autodesk - пусть они поломают себе голову.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение