Тема: Обновить значения атрибутов в блоке

Здравствуйте!
Пишу на C# для Autocad 2005.
Каким способом можно программно редактировать значения текстовых аттрибутов блока.
Свой вариант кода привожу ниже:

  Database _db2 = HostApplicationServices.WorkingDatabase;
            Autodesk.AutoCAD.DatabaseServices.TransactionManager tm2 = _db2.TransactionManager;
            Transaction tr2 = tm2.StartTransaction();
            BlockTable bt = (BlockTable)tr2.GetObject(_db2.BlockTableId, OpenMode.ForRead);
            BlockTableRecord btr = (BlockTableRecord)tr2.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
            foreach (ObjectId obId in btr)
            {
                DBObject dbObj = tr2.GetObject(obId, OpenMode.ForRead);
                  BlockReference blkRef = dbObj as BlockReference;
                  if (blkRef!=null)
                    {
                       BlockTableRecord blkDef = (BlockTableRecord)tr2.GetObject(blkRef.BlockTableRecord,OpenMode.ForRead);
                       WinForms.MessageBox.Show("\nGot a block named " + blkDef.Name);
                       foreach (ObjectId idAtt in blkDef)
                       {
                           Entity ent = (Entity)tr2.GetObject(idAtt, OpenMode.ForNotify);
                           if (ent is AttributeDefinition)
                           {
                               AttributeDefinition attDef = (AttributeDefinition)ent;
                               AttributeReference attRef = new AttributeReference();
                               attRef.SetAttributeFromBlock(attDef, blkRef.BlockTransform);
                               attRef.SetPropertiesFrom(attDef);
                               WinForms.MessageBox.Show(attRef.Tag);
                               if (attDef.Tag == "FILE")
                               {
                                                                   attRef.TextString = this.cadApp.ActiveDocument.FullName;
                               }
                           }
                       }
                    }
            }
            tr2.Commit();

Re: Обновить значения атрибутов в блоке

Попробуй так:

[CommandMethod("AttribChange")]
static public void test() // This method can have any name
{
  // Put your command code here
  Autodesk.AutoCAD.EditorInput.Editor editor = Application.DocumentManager.MdiActiveDocument.Editor;
  Database db = HostApplicationServices.WorkingDatabase;
  Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
  Transaction tr = tm.StartTransaction();
  BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
  // Проход по MODEL_SPACE и анализ всех BlockReference
  foreach (ObjectId obId in btr)
  {
    DBObject dbObj = tr.GetObject(obId, OpenMode.ForRead);
    BlockReference blkRef = dbObj as BlockReference;
    if (blkRef!=null)
    {
      BlockTableRecord blkDef = (BlockTableRecord)tr.GetObject(blkRef.BlockTableRecord,OpenMode.ForRead);
      editor.WriteMessage("\nBlock named: " + blkDef.Name);
      foreach (ObjectId idAtt in blkRef.AttributeCollection)
      {
        Entity ent = (Entity)tr.GetObject(idAtt, OpenMode.ForWrite);
        if (ent is AttributeReference)
        {
          AttributeReference attRef = (AttributeReference) ent;
          // Если имя аттрибута "FILE" - меняем значение на путь к dwg-файлу
          if (attRef.Tag.ToUpper() == "FILE")
          {
            attRef.TextString = db.Filename;
          }
        }
      }
    }
  }
  tr.Commit();
}

Re: Обновить значения атрибутов в блоке

Александр, спасибо за вариант решения проблемы.
Но в моём случае приходится работать с Autocad2005, и с managed сборками для работы с ним, а в  них нет  BlockReference.AttributeCollection.
С уважением,
Кайрат Галиев.

Re: Обновить значения атрибутов в блоке

> Kairat
Ну не так страшен черт, как его малюют. smile
Если воспользоваться BlockReference.GetEnumerator(), то все будет нормально.
Этот код работает и в AutoCAD 2005:

// Define Command "AttribChange"
// For AutoCAD 2005
[CommandMethod("AttribChange")]
static public void test() // This method can have any name
{
  // Put your command code here
  Database db = HostApplicationServices.WorkingDatabase;
  Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
  Transaction tr = tm.StartTransaction();
  BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
  // Проход по MODEL_SPACE и анализ всех BlockReference
  foreach (ObjectId obId in btr)
  {
    DBObject dbObj = tr.GetObject(obId, OpenMode.ForRead);
    BlockReference blkRef = dbObj as BlockReference;
    if (blkRef!=null)
    {
      BlockTableRecord blkDef = (BlockTableRecord)tr.GetObject(blkRef.BlockTableRecord,OpenMode.ForRead);
      IEnumerator i = blkRef.GetEnumerator();
      while (i.MoveNext())
      {
        DBObject ent = (DBObject)tr.GetObject((ObjectId)i.Current, OpenMode.ForWrite);
        if (ent is AttributeReference)
        {
          AttributeReference attRef = (AttributeReference) ent;
          if (attRef.Tag.ToUpper() == "FILE")
          {
            attRef.TextString = db.Filename;
          }
        }
      }
    }
  }
  tr.Commit();
}

Re: Обновить значения атрибутов в блоке

Александр, огромное спасибо!!!!!

Re: Обновить значения атрибутов в блоке

:) Да ладно... Только я не использую .NET и не пишу на C#, так что если будут более серьезные вопросы, то врядли смогу помочь. :(

(изменено: alex, 11 мая 2011г. 04:59:50)

Re: Обновить значения атрибутов в блоке

Доброго времени суток.
воспользовавшись материалом этого поста, написал небольшую корявую программку, которая меняет атрибут динамического блока, но вот незадача: вроде бы все меняется, но в файле не сохраняется. М.б. Вы увидете ошибку?

Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database destDb = doc.Database;
Editor ed = doc.Editor;

OpenFileDialog opFlDlg = new OpenFileDialog();
opFlDlg.Filter = "DWG (*.dwg)|*.dwg|All files (*.*)|*.*";
opFlDlg.Multiselect = true;
opFlDlg.Title = "Select your favorite files";

if (opFlDlg.ShowDialog() == DialogResult.OK)
{
    foreach (string file in opFlDlg.FileNames)
    {
        //doc.Editor.WriteMessage("file: " + file.ToString() + "\n");
        try
        {
            Database sourceDb = new Database(false, true);
            Transaction tr = sourceDb.TransactionManager.StartTransaction();
            using (tr)
            {
                sourceDb.ReadDwgFile(file, System.IO.FileShare.Read, true, "");
                BlockTable bt = (BlockTable)tr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead);
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
                foreach (ObjectId obId in btr)
                {
                    DBObject dbObj = tr.GetObject(obId, OpenMode.ForRead);
                    BlockReference blkRef = dbObj as BlockReference;
                    if (blkRef != null)
                    {
                        BlockTableRecord brDef = (BlockTableRecord)tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead);

                        if (brDef.Name == "shtamp_b")
                        {
                            ed.WriteMessage("\nFile name: {0}; result: true", file.ToString());
                            foreach (ObjectId idAtt in blkRef.AttributeCollection)
                            {
                                Entity ent = (Entity)tr.GetObject(idAtt, OpenMode.ForWrite);
                                if (ent is AttributeReference)
                                {
                                    AttributeReference attRef = (AttributeReference)ent;
                                    if (attRef.Tag.ToUpper() == "STADIY")
                                    {
                                        ed.WriteMessage(attRef.TextString + "/");
                                        attRef.TextString = "U!";
                                        ed.WriteMessage(attRef.TextString + "/");
                                    }
                                }
                            }
                        }
                        else
                            ed.WriteMessage("\nFile name: {0}; result: false", file.ToString());
                    }
                    else
                        ed.WriteMessage("\nFile name: {0}; result: empty blockreference", file.ToString());
                }
                //tr.Commit();
            }
        }
        catch (System.Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
}

при этом тут видно, что новое значение присваивается, однако после окончания работы программы все сбрасывается в прежнее состояние.

ed.WriteMessage(attRef.TextString + "/"); 
attRef.TextString = "U!"; 
ed.WriteMessage(attRef.TextString + "/");

Re: Обновить значения атрибутов в блоке

1) Почему строка закомментарена:

       //tr.Commit(); 

:?:
2) Где sourceDb сохраняется в файл?

Re: Обновить значения атрибутов в блоке

Да, я уже не знаю, зачем коммит закомментировал, но с ним тоже ничего работало.
А коммит() же и должен sourceDB сохранять?
А с этим я ничего не наврал? Объектная модель так строится?

Database sourceDb = new Database(false, true); 
Transaction tr = sourceDb.TransactionManager.StartTransaction(); 
using (tr) 
{ 
    sourceDb.ReadDwgFile(file, System.IO.FileShare.Read, true, ""); 
    BlockTable bt = (BlockTable)tr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead); 
    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);

    ///...

    tr.Commit();
}

Re: Обновить значения атрибутов в блоке

alex пишет:

А коммит() же и должен sourceDB сохранять?

Нет. commit() завершает транзакцию. Если его не выполнить, то никакие изменения в базу (Database) не внесутся. Но если не выполнить sourceDb.saveAs(file,...), то база не будет сохранена на диск (в файл), и все твои изменения не сохранятся.

(изменено: alex, 11 мая 2011г. 20:46:03)

Re: Обновить значения атрибутов в блоке

да, вот это уже интересно.
сделал так, но пишет ошибку eFileSharingViolation. именно на строчке с сохранением и падает. какие-то еще манипуляции необходимы?

 
      } 
      tr.Commit(); 
   }
   sourceDb.saveAs(file, DwgVersion.Current);
} 

при использовании sourceDb.Save() ошибка eFileInternalErr

(изменено: Александр Ривилис, 11 мая 2011г. 22:33:54)

Re: Обновить значения атрибутов в блоке

Попробуй:

sourceDb.CloseInput(true);
sourceDb.saveAs(file, true, DwgVersion.Current, sourceDb.SecurityParameters);  

Если не получится, сохраняй под другим именем.