Author Topic: .NET BLOCK Routines  (Read 66124 times)

0 Members and 2 Guests are viewing this topic.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #15 on: November 23, 2013, 06:03:57 PM »
Hi,

This is a simple and 'brute force' method to mimic the ATTSYNC command. It replaces the existing AttributeReferences from the inserted BlockReferences with new ones from the BlockTableRecord AttributeDefinitions.

The SynchronizeAttributes() method is defined as an extension method for the BlockTableRecord type so that it can be called as an instance method of this type.
Using example (assuming btr is a BlocTableRecord instance) : btr.SynchronizeAttributes()

<EDIT: corrected an issue w/ attributes in dynamic blocks>
<EDIT: corrected an issue w/ constant attributes>

 C# code
Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Runtime;
  5. using AcRx = Autodesk.AutoCAD.Runtime;
  6.  
  7. namespace Autodesk.AutoCAD.DatabaseServices
  8. {
  9.     public static class ExtensionMethods
  10.     {
  11.         static RXClass attDefClass = RXClass.GetClass(typeof(AttributeDefinition));
  12.  
  13.         public static void SynchronizeAttributes(this BlockTableRecord target)
  14.         {
  15.             if (target == null)
  16.                 throw new ArgumentNullException("target");
  17.  
  18.             Transaction tr = target.Database.TransactionManager.TopTransaction;
  19.             if (tr == null)
  20.                 throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
  21.             List<AttributeDefinition> attDefs = target.GetAttributes(tr);
  22.             foreach (ObjectId id in target.GetBlockReferenceIds(true, false))
  23.             {
  24.                 BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
  25.                 br.ResetAttributes(attDefs, tr);
  26.             }
  27.             if (target.IsDynamicBlock)
  28.             {
  29.                 target.UpdateAnonymousBlocks();
  30.                 foreach (ObjectId id in target.GetAnonymousBlockIds())
  31.                 {
  32.                     BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
  33.                     attDefs = btr.GetAttributes(tr);
  34.                     foreach (ObjectId brId in btr.GetBlockReferenceIds(true, false))
  35.                     {
  36.                         BlockReference br = (BlockReference)tr.GetObject(brId, OpenMode.ForWrite);
  37.                         br.ResetAttributes(attDefs, tr);
  38.                     }
  39.                 }
  40.             }
  41.         }
  42.  
  43.         private static List<AttributeDefinition> GetAttributes(this BlockTableRecord target, Transaction tr)
  44.         {
  45.             List<AttributeDefinition> attDefs = new List<AttributeDefinition>();
  46.             foreach (ObjectId id in target)
  47.             {
  48.                 if (id.ObjectClass == attDefClass)
  49.                 {
  50.                     AttributeDefinition attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  51.                     attDefs.Add(attDef);
  52.                 }
  53.             }
  54.             return attDefs;
  55.         }
  56.  
  57.         private static void ResetAttributes(this BlockReference br, List<AttributeDefinition> attDefs, Transaction tr)
  58.         {
  59.             Dictionary<string, string> attValues = new Dictionary<string, string>();
  60.             foreach (ObjectId id in br.AttributeCollection)
  61.             {
  62.                 if (!id.IsErased)
  63.                 {
  64.                     AttributeReference attRef = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite);
  65.                     attValues.Add(attRef.Tag,
  66.                         attRef.IsMTextAttribute ? attRef.MTextAttribute.Contents : attRef.TextString);
  67.                     attRef.Erase();
  68.                 }
  69.             }
  70.             foreach (AttributeDefinition attDef in attDefs)
  71.             {
  72.                 AttributeReference attRef = new AttributeReference();
  73.                 attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  74.                 if (attDef.Constant)
  75.                 {
  76.                     attRef.TextString = attDef.IsMTextAttributeDefinition ?
  77.                         attDef.MTextAttributeDefinition.Contents :
  78.                         attDef.TextString;
  79.                 }
  80.                 else if (attValues.ContainsKey(attRef.Tag))
  81.                 {
  82.                     attRef.TextString = attValues[attRef.Tag];
  83.                 }
  84.                 br.AttributeCollection.AppendAttribute(attRef);
  85.                 tr.AddNewlyCreatedDBObject(attRef, true);
  86.             }
  87.         }
  88.     }
  89. }

VB code (have to clear the Root Namespace in the project to add these extension methods to Autodesk.AutoCAD.DatabaseServices)
Code - vb.net: [Select]
  1. Imports Autodesk.AutoCAD.DatabaseServices
  2. Imports Autodesk.AutoCAD.Runtime
  3.  
  4. Namespace Autodesk.AutoCAD.DatabaseServices
  5.     Public Module ExtensionMethods
  6.  
  7.         Dim attDefClass As RXClass = RXClass.GetClass(GetType(AttributeDefinition))
  8.  
  9.         <System.Runtime.CompilerServices.Extension> _
  10.         Public Sub SynchronizeAttributes(target As BlockTableRecord)
  11.             If target Is Nothing Then
  12.                 Throw New ArgumentNullException("target")
  13.             End If
  14.  
  15.             Dim tr As Transaction = target.Database.TransactionManager.TopTransaction
  16.             If tr Is Nothing Then
  17.                 Throw New Exception(ErrorStatus.NoActiveTransactions)
  18.             End If
  19.  
  20.             Dim attDefs As List(Of AttributeDefinition) = target.GetAttributes(tr)
  21.             For Each id As ObjectId In target.GetBlockReferenceIds(True, False)
  22.                 Dim br As BlockReference = _
  23.                     DirectCast(tr.GetObject(id, OpenMode.ForWrite), BlockReference)
  24.                 br.ResetAttributes(attDefs, tr)
  25.             Next
  26.             If target.IsDynamicBlock Then
  27.                 target.UpdateAnonymousBlocks()
  28.                 For Each id As ObjectId In target.GetAnonymousBlockIds()
  29.                     Dim btr As BlockTableRecord = _
  30.                         DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockTableRecord)
  31.                     attDefs = btr.GetAttributes(tr)
  32.                     For Each brId As ObjectId In btr.GetBlockReferenceIds(True, False)
  33.                         Dim br As BlockReference = _
  34.                             DirectCast(tr.GetObject(brId, OpenMode.ForWrite), BlockReference)
  35.                         br.ResetAttributes(attDefs, tr)
  36.                     Next
  37.                 Next
  38.             End If
  39.         End Sub
  40.  
  41.         <System.Runtime.CompilerServices.Extension> _
  42.         Private Function GetAttributes(target As BlockTableRecord, tr As Transaction) As List(Of AttributeDefinition)
  43.             Dim attdefs As List(Of AttributeDefinition) = New List(Of AttributeDefinition)
  44.             For Each id As ObjectId In target
  45.                 If id.ObjectClass = attDefClass Then
  46.                     Dim attDef As AttributeDefinition = _
  47.                         DirectCast(tr.GetObject(id, OpenMode.ForRead), AttributeDefinition)
  48.                     attdefs.Add(attDef)
  49.                 End If
  50.             Next
  51.             Return attdefs
  52.         End Function
  53.  
  54.         <System.Runtime.CompilerServices.Extension> _
  55.         Private Sub ResetAttributes(br As BlockReference, attDefs As List(Of AttributeDefinition), tr As Transaction)
  56.             Dim attValues As New Dictionary(Of String, String)()
  57.             For Each id As ObjectId In br.AttributeCollection
  58.                 If Not id.IsErased Then
  59.                     Dim attRef As AttributeReference = _
  60.                         DirectCast(tr.GetObject(id, OpenMode.ForWrite), AttributeReference)
  61.                     attValues.Add( _
  62.                         attRef.Tag, _
  63.                         If(attRef.IsMTextAttribute, attRef.MTextAttribute.Contents, attRef.TextString))
  64.                     attRef.Erase()
  65.                 End If
  66.             Next
  67.             For Each attDef As AttributeDefinition In attDefs
  68.                 Dim attRef As New AttributeReference()
  69.                 attRef.SetAttributeFromBlock(attDef, br.BlockTransform)
  70.                 If attDef.Constant Then
  71.                     attRef.TextString = If(attDef.IsMTextAttributeDefinition, _
  72.                                            attDef.MTextAttributeDefinition.Contents, _
  73.                                            attDef.TextString)
  74.                 Else If attValues IsNot Nothing AndAlso attValues.ContainsKey(attDef.Tag) Then
  75.                     attRef.TextString = attValues(attDef.Tag.ToUpper())
  76.                 End If
  77.                 br.AttributeCollection.AppendAttribute(attRef)
  78.                 tr.AddNewlyCreatedDBObject(attRef, True)
  79.             Next
  80.         End Sub
  81.  
  82.     End Module
  83.  
  84. End Namespace
« Last Edit: November 26, 2013, 08:46:35 AM by gile »
Speaking English as a French Frog

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: .NET BLOCK Routines
« Reply #16 on: November 23, 2013, 08:35:23 PM »
Code - C#: [Select]
  1. if (target == null)
  2.                 throw new ArgumentNullException("btr");

is a nice addition gile :)
thanks !
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #17 on: November 24, 2013, 03:09:06 AM »
Thanks for the comment, Kerry. I's corrected now...
Speaking English as a French Frog

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: .NET BLOCK Routines
« Reply #18 on: November 26, 2013, 04:01:15 AM »
I knew what you meant, and it wasn't going to break anything :-)
I was really just commenting favourably on the throwing of the exception , something that doesn't happen much in the public code arena.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #19 on: November 26, 2013, 06:36:34 AM »
Thank again Kerry.

I updated the upper codes to correct an issue with attributes in some dynamic blocks.
Speaking English as a French Frog

Jeff H

  • Needs a day job
  • Posts: 6144
Re: .NET BLOCK Routines
« Reply #20 on: November 26, 2013, 06:43:10 AM »
Nice Gile!

You might want to throw a check in there to see if it is Constant and skip it.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #21 on: November 26, 2013, 08:48:26 AM »
Thanks Jeff.
I've updated the code so that they take into account the constant attributes.
Speaking English as a French Frog

Locke

  • Guest
Re: .NET BLOCK Routines
« Reply #22 on: March 28, 2014, 11:23:26 AM »
Gile,

Would you mind educating me a little on your attribute synchronization approach?  I'm looking at line 20 of the SynchronizeAttributes method.  Correct me if I'm not getting the logic here.  So we're checking the TransactionManager for a top transaction because we want to make sure that our 'GetObject' calls succeed later on in the routine?

However If the BlockTableRecord has been opened using a StartOpenCloseTransaction, then the routine fails even though we have a legitimate hold on it.  Would it make sense to try working with a new transaction if one isn't found?

Here's one way it could be done, albeit sacrificing rollback capabilities in the calling method (which I've never used personally, but to each their own):

Code - C#: [Select]
  1.  
  2. public static void SynchronizeAttributes(this BlockTableRecord target)
  3. {
  4.     if (target == null)
  5.         throw new ArgumentNullException("target");
  6.  
  7.     var topTransaction = target.Database.TransactionManager.TopTransaction;
  8.     var tr = topTransaction ?? target.Database.TransactionManager.StartTransaction();
  9.     if (tr == null)
  10.         throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
  11.  
  12.     try
  13.     {
  14.         List<AttributeDefinition> attDefs = target.GetAttributes(tr);
  15.         foreach (ObjectId id in target.GetBlockReferenceIds(true, false))
  16.         {
  17.             BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
  18.             br.ResetAttributes(attDefs, tr);
  19.         }
  20.         if (target.IsDynamicBlock)
  21.         {
  22.             target.UpdateAnonymousBlocks();
  23.             foreach (ObjectId id in target.GetAnonymousBlockIds())
  24.             {
  25.                 BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
  26.                 attDefs = btr.GetAttributes(tr);
  27.                 foreach (ObjectId brId in btr.GetBlockReferenceIds(true, false))
  28.                 {
  29.                     BlockReference br = (BlockReference)tr.GetObject(brId, OpenMode.ForWrite);
  30.                     br.ResetAttributes(attDefs, tr);
  31.                 }
  32.             }
  33.         }
  34.     }
  35.     catch (Autodesk.AutoCAD.Runtime.Exception ex)
  36.     {
  37.         System.Diagnostics.Debug.Write(ex.Message);
  38.         throw;
  39.     }
  40.     finally
  41.     {
  42.         if (topTransaction == null)
  43.         {
  44.             tr.Commit();
  45.             tr.Dispose();
  46.         }
  47.     }
  48. }
  49.  
  50.  

« Last Edit: March 28, 2014, 11:33:28 AM by Locke »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #23 on: March 28, 2014, 04:45:41 PM »
Locke,

This is probably due to a habit, I wrote this as part of a larger extension methods library in which most methods have to be called from a transaction.
in this library the code looks like this:
Code - C#: [Select]
  1.         public static void SynchronizeAttributes(this BlockTableRecord target)
  2.         {
  3.             if (target == null)
  4.                 throw new ArgumentNullException("target");
  5.  
  6.             AttributeDefinition[] attDefs = target.GetObjects<AttributeDefinition>().ToArray();
  7.             foreach (BlockReference br in target.GetBlockReferenceIds(true, false)
  8.                                                 .GetObjects<BlockReference>(OpenMode.ForWrite))
  9.             {
  10.                 br.ResetAttributes(attDefs);
  11.             }
  12.             if (target.IsDynamicBlock)
  13.             {
  14.                 target.UpdateAnonymousBlocks();
  15.                 foreach (BlockTableRecord btr in target.GetAnonymousBlockIds()
  16.                                                        .GetObjects<BlockTableRecord>())
  17.                 {
  18.                     attDefs = btr.GetObjects<AttributeDefinition>().ToArray();
  19.                     foreach (BlockReference br in btr.GetBlockReferenceIds(true,false)
  20.                                                      .GetObjects<BlockReference>(OpenMode.ForWrite))
  21.                     {
  22.                         br.ResetAttributes(attDefs);
  23.                     }
  24.                 }
  25.             }
  26.         }
The test for the top transaction doesn't appear here beacause it's part of the GetObjects<T>() method.

By my side, I don't use so often OpenCloseTransactionS (in some event handlers).
When I don't care about roll back and try to improve performance, I'd rather use the 'obsolete' Open() method in a using statement.
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #24 on: March 28, 2014, 06:18:28 PM »
I'll try to explain where this habit came from.

Sometimes ago, inspired by some 'masters' (Tony Tanzillo and Kaefer) i tried to build some extension methods as helpers to make the code more concise.

The start was a ObjectId.GetObject<T>() generic method.
The simplest implementation was:
Code - C#: [Select]
  1. public static T Getobject<T> (this ObjectId id) where T : DBObject
  2. {
  3.    return (T)id.GetObject(OpenMode.ForRead);
  4. }
But calling the Objectid.GetObject() method crashes AutoCAD when called from outisde a transaction (as from an OpenCloseTransaction).
Testing for a TopTransaction and throwing an exception if none avoid the fatal error.
In the mean time testing for a TopTransaction allows to get this transaction and use Transaction.GetObject() which is cheaper than ObjectId.GetObject().
So, here's the GetObject<T>() method I use now. The code isn't as concise but safer:
Code - C#: [Select]
  1.         public static T GetObject<T>(
  2.             this ObjectId id,
  3.             OpenMode mode = OpenMode.ForRead,
  4.             bool openErased = false,
  5.             bool onLockedLayers = false)
  6.             where T : DBObject
  7.         {
  8.             if (id == ObjectId.Null)
  9.                 throw new ArgumentNullException("id");
  10.  
  11.             Transaction tr = id.Database.TransactionManager.TopTransaction;
  12.             if (tr == null)
  13.                 throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
  14.  
  15.             return (T)tr.GetObject(id, mode, openErased, onLockedLayers);
  16.         }
Speaking English as a French Frog

Locke

  • Guest
Re: .NET BLOCK Routines
« Reply #25 on: March 28, 2014, 09:15:58 PM »
Gile, thanks for following up with me so thoroughly.  Seeing the rest of the code puts this approach in context.  I'm going to let this soak in for a few hours, and then I might play around with these samples at my end.

Regards,
Locke 

fixo

  • Guest
Re: .NET BLOCK Routines
« Reply #26 on: May 19, 2014, 05:32:01 AM »
Sample code to change attribute Font and synchronize block reference after
Code - C#: [Select]
  1.         ////ads_queueexpr on A2014
  2.         //[DllImport("accore.dll", CharSet = CharSet.Unicode,
  3.         //        CallingConvention = CallingConvention.Cdecl,
  4.         //        EntryPoint = "ads_queueexpr")]
  5.         //extern static private int ads_queueexpr(byte[] command);
  6.         [CommandMethod("chf", CommandFlags.UsePickSet | CommandFlags.Redraw)]
  7.         public static void TestChangeAttributeFont()
  8.         {
  9.  
  10.             Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
  11.  
  12.             Database db = doc.Database;
  13.  
  14.             Editor ed = doc.Editor;
  15.             string blkname = string.Empty;
  16.             using (DocumentLock doclock = doc.LockDocument())
  17.             {
  18.                 using (Transaction trans = db.TransactionManager.StartTransaction())
  19.                 {
  20.                     doc.TransactionManager.EnableGraphicsFlush(true);
  21.                     BlockReference bref = trans.GetObject(ed.GetEntity("\nSelect  block reference").ObjectId, OpenMode.ForRead) as BlockReference;
  22.  
  23.                     if (bref == null)
  24.                     {
  25.                         Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog("Nothing or wrong object selected");
  26.                         return;
  27.                     }
  28.                     TextStyleTable tt = (TextStyleTable)trans.GetObject(db.TextStyleTableId, OpenMode.ForRead);
  29.                     if (!tt.Has("arialstyle"))
  30.                     {
  31.                         tt.UpgradeOpen();
  32.                         TextStyleTableRecord ttr = new TextStyleTableRecord();
  33.                         ttr.Name = "arialstyle";
  34.                         ttr.Font = new Autodesk.AutoCAD.GraphicsInterface.FontDescriptor();// working code block on A2014:
  35.                         ttr.FileName = "arial.ttf";
  36.                         ttr.TextSize = 0.0;
  37.                         // set other options here (bold, italic etc.)
  38.                         tt.Add(ttr);
  39.                         ed.Regen();//optional
  40.                         trans.AddNewlyCreatedDBObject(ttr, true);
  41.                        
  42.                         tt.DowngradeOpen();
  43.                         trans.Commit();
  44.                     }
  45.                     // second transaction
  46.                     using (Transaction trans2 = db.TransactionManager.StartTransaction())
  47.                     {
  48.                         tt = (TextStyleTable)trans2.GetObject(db.TextStyleTableId, OpenMode.ForRead);
  49.                         if (!tt.Has("arialstyle"))
  50.                         {
  51.                             Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog("TextStyle \"arialstyle\" could not added");
  52.                             return;
  53.                         }
  54.                        
  55.                         BlockTable bt = (BlockTable)trans2.GetObject(db.BlockTableId, OpenMode.ForWrite);
  56.  
  57.                         BlockTableRecord btrec = (BlockTableRecord)trans2.GetObject(bref.BlockTableRecord, OpenMode.ForRead, false) as BlockTableRecord;
  58.                          blkname = btrec.Name;
  59.                         if (!btrec.HasAttributeDefinitions) return;
  60.                         btrec.UpgradeOpen();
  61.                         foreach (ObjectId id in btrec)
  62.                         {
  63.                             if (!id.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(AttributeDefinition)))) continue;
  64.                             DBObject obj = trans2.GetObject(id, OpenMode.ForRead);
  65.                             AttributeDefinition attdef = obj as AttributeDefinition;
  66.                             if (attdef.TextStyleId == tt["arialstyle"])
  67.                             {
  68.                                 continue;
  69.                             }
  70.                             else
  71.                             {
  72.                                 Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(attdef.Tag);
  73.                                 attdef.UpgradeOpen();
  74.                                 attdef.TextStyleId = tt["arialstyle"];
  75.                             }
  76.  
  77.                         }
  78.                      
  79.                         trans2.Commit();
  80.                     }
  81.                     // perform attsync command
  82.                     string msg= string.Format("(command \"{0}\" \"{1}\"  \"{2}\")","_.ATTSYNC","_NAME",blkname);
  83.                     System.Text.UnicodeEncoding unEncode = new System.Text.UnicodeEncoding();
  84.                     ads_queueexpr(unEncode.GetBytes(msg));
  85.                    
  86.                 }
  87.             }
  88.         }

BillZndl

  • Guest
Re: .NET BLOCK Routines
« Reply #27 on: August 06, 2014, 08:09:30 AM »
I'm posting this one in response to my own question in THIS THREAD. (see summary section of this file).

C# code.

Code - C#: [Select]
  1. using System;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.Internal;
  7. using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
  8.  
  9. namespace FixBlocks
  10. {
  11.     public class ChangeInsertionPoint
  12.     {
  13.         /// <summary>
  14.         ///Created thanks to the help of many @the Swamp.org.
  15.         ///Intended to change the displayed insertion point of simple 2D blocks in ModelSpace(not sure how it affects dynamic or more complex blocks).
  16.         ///Moves entities contained in block reference to center on the insertion/position point.
  17.         ///but can be used to set the insertion point to any location desired.
  18.         ///*Caution*, this will change the appearance of drawings where the insertion point is away from the displayed entities.
  19.         /// </summary>
  20.  
  21.         public static void ChangeInsertPoint()
  22.         {
  23.             Document document = AcadApp.DocumentManager.MdiActiveDocument;
  24.             Editor editor = document.Editor;
  25.             Database database = document.Database;
  26.            
  27.             using (document.LockDocument())
  28.             {
  29.                 PromptEntityOptions options = new PromptEntityOptions("\nPick a block: ");
  30.                 options.SetRejectMessage("\nMust be a block reference: ");
  31.                 options.AddAllowedClass(typeof(BlockReference), true);
  32.  
  33.                 Utils.PostCommandPrompt();
  34.                 PromptEntityResult result = editor.GetEntity(options);  //select a block reference in the drawing.
  35.  
  36.                 if (result.Status == PromptStatus.OK)
  37.                 {            
  38.                     using (Transaction transaction = database.TransactionManager.StartTransaction())
  39.                     {
  40.                         BlockReference reference = (BlockReference)transaction.GetObject(result.ObjectId, OpenMode.ForRead);
  41.                         BlockTableRecord record = (BlockTableRecord)transaction.GetObject(reference.BlockTableRecord, OpenMode.ForRead);
  42.                        
  43.                         Point3d refPos = reference.Position;                           //current position of inserted block ref.                        
  44.                         Point3d pmin = reference.Bounds.Value.MinPoint;                //bounding box of entities.
  45.                         Point3d pmax = reference.Bounds.Value.MaxPoint;
  46.                         Point3d newPos = (Point3d)(pmin + (pmax - pmin) / 2);          //center point of displayed graphics.
  47.  
  48.                         Vector3d vec = newPos.GetVectorTo(refPos);                     //apply your own desired points here.                  
  49.                         vec = vec.TransformBy(reference.BlockTransform.Transpose());   //
  50.                         Matrix3d mat = Matrix3d.Displacement(vec);                     //    
  51.                                      
  52.  
  53.                         foreach (ObjectId eid in record)                               //update entities in the table record.
  54.                         {
  55.                             Entity entity = (Entity)transaction.GetObject(eid, OpenMode.ForRead) as Entity;
  56.  
  57.                             if (entity != null)
  58.                             {
  59.                                 entity.UpgradeOpen();
  60.                                 entity.TransformBy(mat);
  61.                                 entity.DowngradeOpen();
  62.                             }
  63.                         }
  64.  
  65.                         ObjectIdCollection blockReferenceIds = record.GetBlockReferenceIds(false, false); //get all instances of same block ref.
  66.  
  67.                         foreach (ObjectId eid in blockReferenceIds)              //update all block references of the block modified.
  68.                         {
  69.                             BlockReference BlkRef = (BlockReference)transaction.GetObject(eid, OpenMode.ForWrite);
  70.  
  71.                            // BlkRef.TransformBy(mat.Inverse());  // include this line if you want block ref to stay in original location in dwg.
  72.  
  73.                             BlkRef.RecordGraphicsModified(true);
  74.                         }
  75.  
  76.                         transaction.TransactionManager.QueueForGraphicsFlush();  //
  77.                        
  78.                         editor.WriteMessage("\nInsertion points modified.");     //                              
  79.                        
  80.                         transaction.Commit();                                    //                        
  81.                     }
  82.                 }
  83.                 else
  84.                 {
  85.                     editor.WriteMessage("Nothing picked: *" + result.Status + "*");
  86.                 }
  87.             }
  88.             AcadApp.UpdateScreen();
  89.             Utils.PostCommandPrompt();          
  90.         }              
  91.     }
  92. }
  93.  
  94.  

Edit: removed some worthless lines left in copied file.
Edit: Added line to move block ref to original location.
edit:kdub->csharp code tags added
« Last Edit: August 06, 2014, 06:09:17 PM by Kerry »

flyte

  • Newt
  • Posts: 26
Re: .NET BLOCK Routines
« Reply #28 on: September 10, 2014, 06:01:09 PM »
Hi,

Here's a little example for attributes extraction.

I try to write it in a "declarative" style using the Linq extension methods and some others defined in the Extension class (thanks to Thorsten 'kaefer' @ TheSwamp for its nice 'evil' ones).

[...snip..]
The Extensions class provides extension methods that allows to write code in a more "declarative" style
and methods to build a DataTable from a BlockAttribute collection and convert this DataTable into xls, csv files or AutoCAD Table.
Code: [Select]
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;
using AcDataTable = Autodesk.AutoCAD.DatabaseServices.DataTable;

namespace AttributeExtraction
{
    public static class Extensions
    {
        // Opens a DBObject in ForRead mode (kaefer @ TheSwamp)
        public static T GetObject<T>(this ObjectId id) where T : DBObject
        {
            return id.GetObject<T>(OpenMode.ForRead);
        }

        // Opens a DBObject in the given mode (kaefer @ TheSwamp)
        public static T GetObject<T>(this ObjectId id, OpenMode mode) where T : DBObject
        {
            return id.GetObject(mode) as T;
        }
 
        // Opens a collection of DBObject in ForRead mode (kaefer @ TheSwamp)       
        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids) where T : DBObject
        {
            return ids.GetObjects<T>(OpenMode.ForRead);
        }

        // Opens a collection of DBObject in the given mode (kaefer @ TheSwamp)
        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids, OpenMode mode) where T : DBObject
        {
            return ids
                .Cast<ObjectId>()
                .Select(id => id.GetObject<T>(mode))
                .Where(res => res != null);
        }

        // Applies the given Action to each element of the collection (mimics the F# Seq.iter function).
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection) action(item);
        }

        // Applies the given Action to each element of the collection (mimics the F# Seq.iteri function).
        // The integer passed to the Action indicates the index of element.
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T, int> action)
        {
            int i = 0;
            foreach (T item in collection) action(item, i++);
        }

        // Gets the block effective name (anonymous dynamic blocs).
        public static string GetEffectiveName(this BlockReference br)
        {
            if (br.IsDynamicBlock)
                return br.DynamicBlockTableRecord.GetObject<BlockTableRecord>().Name;
            return br.Name;
        }

        // Creates a System.Data.DataTable from a BlockAttribute collection.
        public static System.Data.DataTable ToDataTable(this IEnumerable<BlockAttribute> blockAtts, string name)
        {
            System.Data.DataTable dTable = new System.Data.DataTable(name);
            dTable.Columns.Add("Name", typeof(string));
            dTable.Columns.Add("Quantity", typeof(int));
            blockAtts
                .GroupBy(blk => blk, (blk, blks) => new { Block = blk, Count = blks.Count() }, new BlockAttributeEqualityComparer())
                .Iterate(row =>
                {
                    System.Data.DataRow dRow = dTable.Rows.Add(row.Block.Name, row.Count);
                    row.Block.Attributes.Iterate(att =>
                    {
                        if (!dTable.Columns.Contains(att.Key))
                            dTable.Columns.Add(att.Key);
                        dRow[att.Key] = att.Value;
                    });
                });
            return dTable;
        }

        // Gets the column names collection of the datatable
        public static IEnumerable<string> GetColumnNames(this System.Data.DataTable dataTbl)
        {
            return dataTbl.Columns.Cast<System.Data.DataColumn>().Select(col => col.ColumnName);
        }

        // Writes an Excel file from the datatable (using late binding)
        public static void WriteXls(this System.Data.DataTable dataTbl, string filename, string sheetName, bool visible)
        {
            object mis = Type.Missing;
            object xlApp = LateBinding.GetOrCreateInstance("Excel.Application");
            xlApp.Set("DisplayAlerts", false);
            object workbooks = xlApp.Get("Workbooks");
            object workbook, worksheet;
            if (File.Exists(filename))
                workbook = workbooks.Invoke("Open", filename);
            else
                workbook = workbooks.Invoke("Add", mis);
            if (string.IsNullOrEmpty(sheetName))
                worksheet = workbook.Get("Activesheet");
            else
            {
                object worksheets = workbook.Get("Worksheets");
                try
                {
                    worksheet = worksheets.Get("Item", sheetName);
                    worksheet.Get("Cells").Invoke("Clear");
                }
                catch
                {
                    worksheet = worksheets.Invoke("Add", mis);
                    worksheet.Set("Name", sheetName);
                }
            }
            object range = worksheet.Get("Cells");
            dataTbl.GetColumnNames()
                .Iterate((name, i) => range.Get("Item", 1, i + 1).Set("Value2", name));
            dataTbl.Rows
                .Cast<DataRow>()
                .Iterate((row, i) => row.ItemArray
                    .Iterate((item, j) => range.Get("Item", i + 2, j + 1).Set("Value2", item)));
            xlApp.Set("DisplayAlerts", true);
            if (visible)
            {
                xlApp.Set("Visible", true);
            }
            else
            {
                if (File.Exists(filename))
                    workbook.Invoke("Save");
                else
                {
                    int fileFormat =
                        string.Compare("11.0", (string)xlApp.Get("Version")) < 0 &&
                        filename.EndsWith(".xlsx", StringComparison.CurrentCultureIgnoreCase) ?
                        51 : -4143;
                    workbook.Invoke("Saveas", filename, fileFormat, string.Empty, string.Empty, false, false, 1, 1);
                }
                workbook.Invoke("Close");
                workbook = null;
                xlApp.ReleaseInstance();
                xlApp = null;
            }
        }

        // Writes a csv file from the datatable.
        public static void WriteCsv(this System.Data.DataTable dataTbl, string filename)
        {
            using (StreamWriter writer = new StreamWriter(filename))
            {
                writer.WriteLine(dataTbl.GetColumnNames().Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)));
                dataTbl.Rows
                    .Cast<DataRow>()
                    .Select(row => row.ItemArray.Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)))
                    .Iterate(line => writer.WriteLine(line));
            }
        }

        // Creates an AutoCAD Table from the datatable.
        public static Table ToAcadTable(this System.Data.DataTable dataTbl, double rowHeight, double columnWidth)
        {
            //return dataTbl.Rows.Cast<DataRow>().ToAcadTable(dataTbl.TableName, dataTbl.GetColumnNames(), rowHeight, columnWidth);
            Table tbl = new Table();
            tbl.Rows[0].Height = rowHeight;
            tbl.Columns[0].Width = columnWidth;
            tbl.InsertColumns(0, columnWidth, dataTbl.Columns.Count - 1);
            tbl.InsertRows(0, rowHeight, dataTbl.Rows.Count + 1);
            tbl.Cells[0, 0].Value = dataTbl.TableName;
            dataTbl.GetColumnNames()
                .Iterate((name, i) => tbl.Cells[1, i].Value = name);
            dataTbl.Rows
                .Cast<DataRow>()
                .Iterate((row, i) =>
                    row.ItemArray.Iterate((item, j) =>
                        tbl.Cells[i + 2, j].Value = item));
            return tbl;
        }
    }
}

wow.. this post blew me away and I signed up to the swamp just for this.

Does anyone know if this can obtain attributes from nested blocks? When I try to test it out, it doesn't appear to find nested blocks.

Is the only way to obtain the attributes of nested blocks is to do it by recursion? I'm stuck using AutoCAD 2010, and thus .NET 3.5, so any nifty dynamic solutions won't work for me.

Any help would be greatly appreciated.

flyte

  • Newt
  • Posts: 26
Re: .NET BLOCK Routines
« Reply #29 on: October 02, 2014, 11:53:07 AM »
...does anyone have a suggestion or comment about how to find items within a nested block!?