Реализация AttSync на .Net

Дата публикации: 09.10.2011
Дата последнего изменения: 18.10.2011 14:40

    Не знаю, почему в AutoCAD .Net API отсутствует метод, выполняющий ту же функцию, что и известная команда _.attsync. Поскольку функционал нужный - я пишу свой вариант его реализации. Далее собственно сам код.

Александр Ривилис предложил написать простой и удобный класс (WorkingDatabaseSwitcher), который в дальнейшем очень часто можно будет использовать при программировании. Этот класс пригодится и в решении нашей задачи (в комментариях пример его использования).

Класс  WorkingDatabaseSwitcher : IDisposable

   1:  using System;
   2:  using Autodesk.AutoCAD.DatabaseServices;
   3:   
   4:  namespace Bushman.AutoCAD.DatabaseServices {
   5:   
   6:      /// <summary>
   7:      /// Изменяя базу данных чертежей, очень важно контролировать то, какая база данных является текущей. 
   8:      /// Класс <c>WorkingDatabaseSwitcher</c>
   9:      /// берёт на себя контроль над тем, чтобы текущей была именно та база данных, которая нужна.
  10:      /// </summary>
  11:      /// <example>
  12:      /// Пример использования класса:
  13:      /// <code>
  14:      /// //db - объект Database
  15:      /// using (WorkingDatabaseSwitcher hlp = new WorkingDatabaseSwitcher(db)) {
  16:      ///     // тут наш код</code>
  17:      /// }</example>
  18:      public sealed class WorkingDatabaseSwitcher : IDisposable {
  19:          private Database prevDb = null;
  20:   
  21:          /// <summary>
  22:          /// База данных, в контексте которой должна производиться работа. Эта база данных на время становится текущей.
  23:          /// По завершению работы текущей станет та база, которая была ею до этого.
  24:          /// </summary>
  25:          /// <param name="db">База данных, которая должна быть установлена текущей</param>
  26:          public WorkingDatabaseSwitcher(Database db) {
  27:              prevDb = HostApplicationServices.WorkingDatabase;
  28:              HostApplicationServices.WorkingDatabase = db;
  29:          }
  30:   
  31:          /// <summary>
  32:          /// Возвращаем свойству <c>HostApplicationServices.WorkingDatabase</c> прежнее значение
  33:          /// </summary>
  34:          public void Dispose() {
  35:              HostApplicationServices.WorkingDatabase = prevDb;
  36:          }
  37:      }
  38:  }

Переходим к следующему этапу (обратите внимание на то, что использование WorkingDatabaseSwitcher позволяет нам не держать в голове тот момент, что нам нужно контролировать то, какая база данных рабочая)...

Код на C#:

   1:  //Microsoft
   2:  using System;
   3:  using System.Collections.Generic;
   4:  using System.Collections;
   5:  using System.Linq;
   6:  using System.Text;
   7:  using System.ComponentModel;
   8:  using System.Globalization;
   9:  //AutoCAD
  10:  using acad = Autodesk.AutoCAD.ApplicationServices.Application;
  11:  using Autodesk.AutoCAD.ApplicationServices;
  12:  using Autodesk.AutoCAD.DatabaseServices;
  13:  using Autodesk.AutoCAD.EditorInput;
  14:  using Autodesk.AutoCAD.Runtime;
  15:  using Autodesk.AutoCAD.Geometry;
  16:   
  17:  namespace Bushman.AutoCAD.DatabaseServices {
  18:      /// <summary>
  19:      /// Методы расширений для объектов класса Autodesk.AutoCAD.DatabaseServices.BlockTableRecord
  20:      /// </summary>
  21:      public static class BlockTableRecordExtensionMethods {
  22:          /// <summary>
  23:          /// Синхронизация вхождений блоков с их определением
  24:          /// </summary>
  25:          /// <param name="btr">Запись таблицы блоков, принятая за определение блока</param>
  26:          /// <param name="directOnly">Следует ли искать только на верхнем уровне, или же нужно 
  27:          /// анализировать и вложенные вхождения, т.е. следует ли рекурсивно обрабатывать блок в блоке:
  28:          /// true - только верхний; false - рекурсивно проверять вложенные блоки.</param>
  29:          /// <param name="removeSuperfluous">
  30:          /// Следует ли во вхождениях блока удалять лишние атрибуты (те, которых нет в определении блока).</param>
  31:          /// <param name="setAttDefValues">
  32:          /// Следует ли всем атрибутам, во вхождениях блока, назначить текущим значением значение по умолчанию.</param>
  33:          public static void AttSync(this BlockTableRecord btr, bool directOnly, bool removeSuperfluous, bool setAttDefValues) {
  34:              Database db = btr.Database;
  35:              using (WorkingDatabaseSwitcher wdb = new WorkingDatabaseSwitcher(db)) {
  36:                  using (Transaction t = db.TransactionManager.StartTransaction()) {
  37:                      BlockTable bt = (BlockTable) t.GetObject(db.BlockTableId, OpenMode.ForRead);
  38:   
  39:                      //Получаем все определения атрибутов из определения блока
  40:                      IEnumerable<AttributeDefinition> attdefs = btr.Cast<ObjectId>()
  41:                          .Where(n => n.ObjectClass.Name == "AcDbAttributeDefinition")
  42:                          .Select(n => (AttributeDefinition) t.GetObject(n, OpenMode.ForRead))
  43:                          .Where(n => !n.Constant);//Исключаем константные атрибуты, т.к. для них AttributeReference не создаются.
  44:   
  45:                      //В цикле перебираем все вхождения искомого определения блока
  46:                      foreach (ObjectId brId in btr.GetBlockReferenceIds(directOnly, false)) {
  47:                          BlockReference br = (BlockReference) t.GetObject(brId, OpenMode.ForWrite);
  48:   
  49:                          //Проверяем имена на соответствие. В том случае, если вхождение блока "A" вложено в определение блока "B", 
  50:                          //то вхождения блока "B" тоже попадут в выборку. Нам нужно их исключить из набора обрабатываемых объектов 
  51:                          //- именно поэтому проверяем имена.
  52:                          if (br.Name != btr.Name)
  53:                              continue;
  54:   
  55:                          //Получаем все атрибуты вхождения блока
  56:                          IEnumerable<AttributeReference> attrefs = br.AttributeCollection.Cast<ObjectId>()
  57:                              .Select(n => (AttributeReference) t.GetObject(n, OpenMode.ForWrite));
  58:   
  59:                          //Тэги существующих определений атрибутов
  60:                          IEnumerable<string> dtags = attdefs.Select(n => n.Tag);
  61:                          //Тэги существующих атрибутов во вхождении
  62:                          IEnumerable<string> rtags = attrefs.Select(n => n.Tag);
  63:   
  64:                          //Если требуется - удаляем те атрибуты, для которых нет определения 
  65:                          //в составе определения блока
  66:                          if (removeSuperfluous)
  67:                              foreach (AttributeReference attref in attrefs.Where(n => rtags
  68:                                  .Except(dtags).Contains(n.Tag)))
  69:                                  attref.Erase(true);
  70:   
  71:                          //Свойства существующих атрибутов синхронизируем со свойствами их определений
  72:                          foreach (AttributeReference attref in attrefs.Where(n => dtags
  73:                              .Join(rtags, a => a, b => b, (a, b) => a).Contains(n.Tag))) {
  74:                              AttributeDefinition ad = attdefs.First(n => n.Tag == attref.Tag);
  75:   
  76:                              //Метод SetAttributeFromBlock, используемый нами далее в коде, сбрасывает
  77:                              //текущее значение многострочного атрибута. Поэтому запоминаем это значение,
  78:                              //чтобы восстановить его сразу после вызова SetAttributeFromBlock.
  79:                              string value = attref.TextString;
  80:                              attref.SetAttributeFromBlock(ad, br.BlockTransform);
  81:                              //Восстанавливаем значение атрибута
  82:                              attref.TextString = value;
  83:   
  84:                              if (attref.IsMTextAttribute) {
  85:   
  86:                              }
  87:   
  88:                              //Если требуется - устанавливаем для атрибута значение по умолчанию
  89:                              if (setAttDefValues)
  90:                                  attref.TextString = ad.TextString;
  91:   
  92:                              attref.AdjustAlignment(db);
  93:                          }
  94:   
  95:                          //Если во вхождении блока отсутствуют нужные атрибуты - создаём их
  96:                          IEnumerable<AttributeDefinition> attdefsNew = attdefs.Where(n => dtags
  97:                              .Except(rtags).Contains(n.Tag));
  98:   
  99:                          foreach (AttributeDefinition ad in attdefsNew) {
 100:                              AttributeReference attref = new AttributeReference();
 101:                              attref.SetAttributeFromBlock(ad, br.BlockTransform);
 102:                              attref.AdjustAlignment(db);
 103:                              br.AttributeCollection.AppendAttribute(attref);
 104:                              t.AddNewlyCreatedDBObject(attref, true);
 105:                          }
 106:                      }
 107:                      btr.UpdateAnonymousBlocks();
 108:                      t.Commit();
 109:                  }
 110:                  //Если это динамический блок
 111:                  if (btr.IsDynamicBlock) {
 112:                      using (Transaction t = db.TransactionManager.StartTransaction()) {
 113:                          foreach (ObjectId id in btr.GetAnonymousBlockIds()) {
 114:                              BlockTableRecord _btr = (BlockTableRecord) t.GetObject(id, OpenMode.ForWrite);
 115:   
 116:                              //Получаем все определения атрибутов из оригинального определения блока
 117:                              IEnumerable<AttributeDefinition> attdefs = btr.Cast<ObjectId>()
 118:                                  .Where(n => n.ObjectClass.Name == "AcDbAttributeDefinition")
 119:                                  .Select(n => (AttributeDefinition) t.GetObject(n, OpenMode.ForRead));
 120:   
 121:                              //Получаем все определения атрибутов из определения анонимного блока
 122:                              IEnumerable<AttributeDefinition> attdefs2 = _btr.Cast<ObjectId>()
 123:                                  .Where(n => n.ObjectClass.Name == "AcDbAttributeDefinition")
 124:                                  .Select(n => (AttributeDefinition) t.GetObject(n, OpenMode.ForWrite));
 125:   
 126:                              //Определения атрибутов анонимных блоков следует синхронизировать 
 127:                              //с определениями атрибутов основного блока
 128:   
 129:                              //Тэги существующих определений атрибутов
 130:                              IEnumerable<string> dtags = attdefs.Select(n => n.Tag);
 131:                              IEnumerable<string> dtags2 = attdefs2.Select(n => n.Tag);
 132:   
 133:                              //1. Удаляем лишние
 134:                              foreach (AttributeDefinition attdef in attdefs2.Where(n => !dtags.Contains(n.Tag))) {
 135:                                  attdef.Erase(true);
 136:                              }
 137:   
 138:                              //2. Синхронизируем существующие
 139:                              foreach (AttributeDefinition attdef in attdefs.Where(n => dtags
 140:                                  .Join(dtags2, a => a, b => b, (a, b) => a).Contains(n.Tag))) {
 141:                                  AttributeDefinition ad = attdefs2.First(n => n.Tag == attdef.Tag);
 142:                                  ad.Position = attdef.Position;
 143:                                  ad.TextStyle = attdef.TextStyle;
 144:                                  //Если требуется - устанавливаем для атрибута значение по умолчанию
 145:                                  if (setAttDefValues)
 146:                                      ad.TextString = attdef.TextString;
 147:   
 148:                                  ad.Tag = attdef.Tag;
 149:                                  ad.Prompt = attdef.Prompt;
 150:   
 151:                                  ad.LayerId = attdef.LayerId;
 152:                                  ad.Rotation = attdef.Rotation;
 153:                                  ad.LinetypeId = attdef.LinetypeId;
 154:                                  ad.LineWeight = attdef.LineWeight;
 155:                                  ad.LinetypeScale = attdef.LinetypeScale;
 156:                                  ad.Annotative = attdef.Annotative;
 157:                                  ad.Color = attdef.Color;
 158:                                  ad.Height = attdef.Height;
 159:                                  ad.HorizontalMode = attdef.HorizontalMode;
 160:                                  ad.Invisible = attdef.Invisible;
 161:                                  ad.IsMirroredInX = attdef.IsMirroredInX;
 162:                                  ad.IsMirroredInY = attdef.IsMirroredInY;
 163:                                  ad.Justify = attdef.Justify;
 164:                                  ad.LockPositionInBlock = attdef.LockPositionInBlock;
 165:                                  ad.MaterialId = attdef.MaterialId;
 166:                                  ad.Oblique = attdef.Oblique;
 167:                                  ad.Thickness = attdef.Thickness;
 168:                                  ad.Transparency = attdef.Transparency;
 169:                                  ad.VerticalMode = attdef.VerticalMode;
 170:                                  ad.Visible = attdef.Visible;
 171:                                  ad.WidthFactor = attdef.WidthFactor;
 172:   
 173:                                  ad.CastShadows = attdef.CastShadows;
 174:                                  ad.Constant = attdef.Constant;
 175:                                  ad.FieldLength = attdef.FieldLength;
 176:                                  ad.ForceAnnoAllVisible = attdef.ForceAnnoAllVisible;
 177:                                  ad.Preset = attdef.Preset;
 178:                                  ad.Prompt = attdef.Prompt;
 179:                                  ad.Verifiable = attdef.Verifiable;
 180:   
 181:                                  ad.AdjustAlignment(db);
 182:                              }
 183:   
 184:                              //3. Добавляем недостающие
 185:                              foreach (AttributeDefinition attdef in attdefs.Where(n => !dtags2.Contains(n.Tag))) {
 186:                                  AttributeDefinition ad = new AttributeDefinition();
 187:                                  ad.SetDatabaseDefaults();
 188:                                  ad.Position = attdef.Position;
 189:                                  ad.TextStyle = attdef.TextStyle;
 190:                                  ad.TextString = attdef.TextString;
 191:                                  ad.Tag = attdef.Tag;
 192:                                  ad.Prompt = attdef.Prompt;
 193:   
 194:                                  ad.LayerId = attdef.LayerId;
 195:                                  ad.Rotation = attdef.Rotation;
 196:                                  ad.LinetypeId = attdef.LinetypeId;
 197:                                  ad.LineWeight = attdef.LineWeight;
 198:                                  ad.LinetypeScale = attdef.LinetypeScale;
 199:                                  ad.Annotative = attdef.Annotative;
 200:                                  ad.Color = attdef.Color;
 201:                                  ad.Height = attdef.Height;
 202:                                  ad.HorizontalMode = attdef.HorizontalMode;
 203:                                  ad.Invisible = attdef.Invisible;
 204:                                  ad.IsMirroredInX = attdef.IsMirroredInX;
 205:                                  ad.IsMirroredInY = attdef.IsMirroredInY;
 206:                                  ad.Justify = attdef.Justify;
 207:                                  ad.LockPositionInBlock = attdef.LockPositionInBlock;
 208:                                  ad.MaterialId = attdef.MaterialId;
 209:                                  ad.Oblique = attdef.Oblique;
 210:                                  ad.Thickness = attdef.Thickness;
 211:                                  ad.Transparency = attdef.Transparency;
 212:                                  ad.VerticalMode = attdef.VerticalMode;
 213:                                  ad.Visible = attdef.Visible;
 214:                                  ad.WidthFactor = attdef.WidthFactor;
 215:   
 216:                                  ad.CastShadows = attdef.CastShadows;
 217:                                  ad.Constant = attdef.Constant;
 218:                                  ad.FieldLength = attdef.FieldLength;
 219:                                  ad.ForceAnnoAllVisible = attdef.ForceAnnoAllVisible;
 220:                                  ad.Preset = attdef.Preset;
 221:                                  ad.Prompt = attdef.Prompt;
 222:                                  ad.Verifiable = attdef.Verifiable;
 223:   
 224:                                  _btr.AppendEntity(ad);
 225:                                  t.AddNewlyCreatedDBObject(ad, true);
 226:                                  ad.AdjustAlignment(db);
 227:                              }
 228:                              //Синхронизируем все вхождения данного анонимного определения блока
 229:                              _btr.AttSync(directOnly, removeSuperfluous, setAttDefValues);
 230:                          }
 231:                          //Обновляем геометрию определений анонимных блоков, полученных на основе 
 232:                          //этого динамического блока
 233:                          btr.UpdateAnonymousBlocks();
 234:                          t.Commit();
 235:                      }
 236:                  }
 237:              }
 238:          }
 239:      }
 240:  }

Метод для тестов:

   1:  [CommandMethod("MySynch", CommandFlags.Modal)]
   2:  public void MySynch() {
   3:      Document dwg = acad.DocumentManager.MdiActiveDocument;
   4:      Editor ed = dwg.Editor;
   5:      Database db = dwg.Database;
   6:      TypedValue[] types = new TypedValue[] { new TypedValue((int) DxfCode.Start, "INSERT") };
   7:      SelectionFilter sf = new SelectionFilter(types);
   8:      PromptSelectionOptions pso = new PromptSelectionOptions();
   9:      pso.MessageForAdding = "Select block reference";
  10:      pso.SingleOnly = true;
  11:      pso.AllowDuplicates = false; 
  12:      
  13:      PromptSelectionResult psr = ed.GetSelection(pso, sf);
  14:      //*******************************
  15:      if (psr.Status == PromptStatus.OK) {
  16:          using (Transaction t = db.TransactionManager.StartTransaction()) {
  17:              BlockTable bt = (BlockTable) t.GetObject(db.BlockTableId, OpenMode.ForRead);
  18:              BlockReference br = (BlockReference) t.GetObject(psr.Value[0].ObjectId, OpenMode.ForRead);
  19:              BlockTableRecord btr = (BlockTableRecord) t.GetObject(bt[br.Name], OpenMode.ForRead);
  20:              btr.AttSync(false, true, false);
  21:              t.Commit();
  22:          }
  23:      }
  24:      else
  25:          ed.WriteMessage("Bad selectionа.\n");
  26:      //********************************
  27:  }



Comments