Author Topic: Read/Write Block attribute values  (Read 2054 times)

0 Members and 1 Guest are viewing this topic.

cadplayer

  • Bull Frog
  • Posts: 390
  • Autocad Civil3d, OpenDCL.Runtime, LISP, .NET (C#)
Read/Write Block attribute values
« on: November 16, 2016, 06:02:02 AM »
Hello!
Iīm not sure I do it right from beginning, I would like read many Drawings with db.ReadDwgFile(filename, System.IO.FileShare.Read, true, ""); and get all block with name "stamp" on different Layouts in these Drawings. The block stamp have attributes I want read/write. My problem is this:
Code: [Select]
using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt =
                    (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (bt.Has(blockname))
                {
                    BlockTableRecord btr =
                        (BlockTableRecord)tr.GetObject(bt[blockname], OpenMode.ForRead);

                    foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))

the method GetBlockReferenceIds give me 13 objects, I understand 13 objects founds with name "stamp". But when I look in the attributs from refId
Code: [Select]
foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
                    {
                        BlockReference br = (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
                        ent = AcDbExtensionMethods.GetAttributesToDictionaryKeyByTag(br);
                        result.Add(ent);
                    }

public static Dictionary<string, DBText> GetAttributesToDictionaryKeyByTag(this BlockReference blockref)
        {
            return blockref.GetAttributes().ToDictionary(a => GetTag(a), StringComparer.OrdinalIgnoreCase);
        }


I get always the same blockattributesvalues but different ObjectIds how can that be ?



My work I will do to read these Drawing with block "stamp" and could change values and save them, thanks for your support!

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Read/Write Block attribute values
« Reply #1 on: November 16, 2016, 06:38:36 AM »
Hi,

From what you show, the variable ent is of type Dictionary<string, DBText> which is a reference type, so, foreach block reference, you reset the contents of ent even in the result collection.

Try this after removing the ent variable declaration:
Code - C#: [Select]
  1. foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
  2. {
  3.     BlockReference br = (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
  4.     Dictionary<string, DBText> ent = br.GetAttributesToDictionaryKeyByTag();
  5.     result.Add(ent);
  6. }

or simply:
Code - C#: [Select]
  1. foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
  2. {
  3.     BlockReference br = (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
  4.     result.Add(br.GetAttributesToDictionaryKeyByTag());
  5. }

Note the GetAttributesToDictionaryKeyByTag() extension method can be called as an instance method.
Speaking English as a French Frog

cadplayer

  • Bull Frog
  • Posts: 390
  • Autocad Civil3d, OpenDCL.Runtime, LISP, .NET (C#)
Re: Read/Write Block attribute values
« Reply #2 on: November 17, 2016, 02:10:29 AM »
thanks Gile for your explanation.
I am little confused how I can understand Acad-object-structur from block with attributes. For exampel I have a drawing with 10 layouts which have block "stamp" within 14 attributes. I think it works so:
1) Look in Blocktable after blockname "stamp"
2) Get Blocktablerecord of them
3) Get all Blockreferences (I do not realy understand what it means: is there all 10 blocks or 14 attributes)

Code: [Select]
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt =
                    (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (bt.Has(blockname))
                {
                    BlockTableRecord btr =
                        (BlockTableRecord)tr.GetObject(bt[blockname], OpenMode.ForRead);

                    foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
                    {
                        BlockReference br = (BlockReference)tr.GetObject(refId, OpenMode.ForRead);

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Read/Write Block attribute values
« Reply #3 on: November 17, 2016, 04:21:48 AM »
Hi,

The BlockTable may contain 0 or 1 BlockTableRecord (block definition) named "stamp".
If it has this block definition, the BlockTableRecord.GetBlockReferenceIds() method will return all the inserted block references IDs (10).
Each BlockReference object should have an AttributeCollection which contains its attribute references IDs (14).
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Read/Write Block attribute values
« Reply #4 on: November 17, 2016, 05:12:20 AM »
Here's a little example using some extension methods to make the test command code more self explanatory.

Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System;
  6. using System.Collections.Generic;
  7. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  8.  
  9. [assembly: CommandClass(typeof(BlockAttributeSample.Commands))]
  10.  
  11. namespace BlockAttributeSample
  12. {
  13.     public class Commands
  14.     {
  15.         // test command
  16.         [CommandMethod("Test")]
  17.         public void Test()
  18.         {
  19.             var doc = AcAp.DocumentManager.MdiActiveDocument;
  20.             var db = doc.Database;
  21.             var ed = doc.Editor;
  22.  
  23.             using (var tr = db.TransactionManager.StartTransaction())
  24.             {
  25.                 foreach (var br in db.GetBlockReferences("stamp"))
  26.                 {
  27.                     var owner = br.OwnerId.GetObject<BlockTableRecord>();
  28.                     if (owner.IsLayout)
  29.                         ed.WriteMessage("\n" + owner.LayoutId.GetObject<Layout>().LayoutName);
  30.                     else
  31.                         ed.WriteMessage("\n" + owner.Name);
  32.                     foreach (var att in br.GetAttributes())
  33.                     {
  34.                         ed.WriteMessage($"\n\t{att.Tag}: {att.TextString}");
  35.                     }
  36.                 }
  37.                 tr.Commit();
  38.             }
  39.         }
  40.     }
  41.  
  42.     // extension methods
  43.     static class extension
  44.     {
  45.         // returns the object (opened in the specified mode)
  46.         public static T GetObject<T>(
  47.             this ObjectId id,
  48.             OpenMode mode = OpenMode.ForRead,
  49.             bool openErased = false,
  50.             bool forceOpenOnLockedLayer = false) where T : DBObject
  51.         {
  52.             if (id == ObjectId.Null)
  53.                 throw new ArgumentNullException(nameof(id));
  54.  
  55.             var tr = id.Database.TransactionManager.TopTransaction;
  56.             if (tr == null)
  57.                 throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
  58.  
  59.             return (T)tr.GetObject(id, mode, openErased, forceOpenOnLockedLayer);
  60.         }
  61.  
  62.         // gets all references of the block (opened for read)
  63.         public static IEnumerable<BlockReference> GetBlockReferences(this Database db, string blockName)
  64.         {
  65.             if (db == null)
  66.                 throw new ArgumentNullException(nameof(db));
  67.  
  68.             var tr = db.TransactionManager.TopTransaction;
  69.             if (tr == null)
  70.                 throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
  71.  
  72.             var bt = db.BlockTableId.GetObject< BlockTable>();
  73.             if (bt.Has(blockName))
  74.             {
  75.                 var btr = bt[blockName].GetObject<BlockTableRecord>();
  76.                 foreach (ObjectId id in btr.GetBlockReferenceIds(true, false))
  77.                 {
  78.                     yield return id.GetObject<BlockReference>();
  79.                 }
  80.                 if (btr.IsDynamicBlock)
  81.                 {
  82.                     foreach (ObjectId btrId in btr.GetAnonymousBlockIds())
  83.                     {
  84.                         var anonBtr = btrId.GetObject<BlockTableRecord>();
  85.                         foreach (ObjectId id in anonBtr.GetBlockReferenceIds(true, false))
  86.                         {
  87.                             yield return id.GetObject<BlockReference>();
  88.                         }
  89.                     }
  90.                 }
  91.             }
  92.         }
  93.  
  94.         // gets the attribute references (opened for read)
  95.         public static IEnumerable<AttributeReference> GetAttributes(this BlockReference source)
  96.         {
  97.             if (source == null)
  98.                 throw new ArgumentNullException(nameof(source));
  99.  
  100.             var tr = source.Database.TransactionManager.TopTransaction;
  101.             if (tr == null)
  102.                 throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
  103.  
  104.             foreach (ObjectId id in source.AttributeCollection)
  105.             {
  106.                 yield return id.GetObject< AttributeReference>();
  107.             }
  108.         }
  109.     }
  110. }
« Last Edit: November 17, 2016, 07:31:45 AM by gile »
Speaking English as a French Frog

cadplayer

  • Bull Frog
  • Posts: 390
  • Autocad Civil3d, OpenDCL.Runtime, LISP, .NET (C#)
Re: Read/Write Block attribute values
« Reply #5 on: November 17, 2016, 10:07:30 AM »
Ooi great thank you for your work, I have to look to this nice stuff in the evening.

cadplayer

  • Bull Frog
  • Posts: 390
  • Autocad Civil3d, OpenDCL.Runtime, LISP, .NET (C#)
Re: Read/Write Block attribute values
« Reply #6 on: November 18, 2016, 03:18:02 AM »
One think I donīt get, what means nameof(id), nameof(db), nameof(source) ? Is it a method you forget to declare ?

A the problem is I work with VS2013 and C#5.0 I guess nameof is declared in C#6.0. Is there a way to get get it in 4.5 framework?
« Last Edit: November 18, 2016, 03:24:05 AM by cadplayer »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Read/Write Block attribute values
« Reply #7 on: November 18, 2016, 03:54:44 AM »
Hi,

Just replace nameof(xyz) with "xyz".

IMO, you should use VS2015, C# 6 has some (very) nice new features.
These new features have nothing to do with the targeted framework, there're mostly syntactic sugar.
« Last Edit: November 18, 2016, 04:00:21 AM by gile »
Speaking English as a French Frog

cadplayer

  • Bull Frog
  • Posts: 390
  • Autocad Civil3d, OpenDCL.Runtime, LISP, .NET (C#)
Re: Read/Write Block attribute values
« Reply #8 on: November 18, 2016, 05:00:23 AM »
Great I tested your application, it helps very much and I get my problem solved why a specially attribut of "stamp" have always same value. Itīs because attribute use a value as Autocad-Textfield(variable ctab) and here it comes to conflict I get for every stamp-block the same attributvalue. Thatīs why I was thinking I get always same block.
Thank you Gile, regards to France!