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

0 Members and 2 Guests are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
.NET BLOCK Routines
« on: January 27, 2010, 03:05:23 AM »
LIBRARY THREAD for  AutoCAD BLOCKS
 Members are encouraged to post any functions, methods, snips regarding
AutoCAD BLOCKS in .NET : C# ,  VB , F# , Python , etc

Feel free to include comments, descriptive notes, limitations,  and images to document your post.

Please post questions in a regular thread.
« Last Edit: January 27, 2010, 03:12:17 AM by Kerry Brown »
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.

fixo

  • Guest
Re: .NET BLOCK Routines
« Reply #1 on: January 28, 2010, 07:15:10 AM »
Here is a very basic example how to create block correct I hope :)
Code: [Select]

using System;
using System.Collections.Generic;
using System.Text;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.EditorInput;
................................................. 
      public static ObjectId CreateLabelBlock(string blockname)
        {
            ObjectId blkid = ObjectId.Null;
            try
            {

                Database db = HostApplicationServices.WorkingDatabase;
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {

                    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite, false);
                    if (bt.Has(blockname))
                    {
                        blkid = bt[blockname];
                    }
                    else
                    {

                        BlockTableRecord btr = new BlockTableRecord();
                        btr.Name = blockname;
                        btr.Explodable = true;
                        btr.Origin = new Point3d(0, 0, 0);
                        btr.Units = UnitsValue.Inches;
                        btr.BlockScaling = BlockScaling.Any;
                        Line line = new Line(new Point3d(-2, -0.4, 0), new Point3d(2, -0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        line = new Line(new Point3d(-2, 0.4, 0), new Point3d(2, 0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        line = new Line(new Point3d(2, -0.4, 0), new Point3d(2, 0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        line = new Line(new Point3d(-2, -0.4, 0), new Point3d(-2, 0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        AcadApp.SetSystemVariable("CECOLOR", "ByBlock");
                       
                        AttributeDefinition ad = new AttributeDefinition();
                        ad.Layer = "0";
                        //by default applied a current text style
                        ad.Tag = "DETAIL";
                        ad.TextString = "X-Ladder";
                        ad.Prompt = "Detail name";

                        ad.Position = new Point3d(0, 0, 0);
                        ad.HorizontalMode = TextHorizontalMode.TextMid;
                        ad.VerticalMode = TextVerticalMode.TextVerticalMid;
                        ad.AlignmentPoint = ad.Position;
                        btr.AppendEntity(ad);

                        blkid = bt.Add(btr);
                        AcadApp.SetSystemVariable("CECOLOR", "ByLayer");
                        tr.AddNewlyCreatedDBObject(btr, true);
                    }
                    tr.Commit();
                }

            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex)
            {
                AcadApp.ShowAlertDialog("ERROR: " + "\n" + ex.Message + "\nSOURCE: " + ex.StackTrace);
            }
            return blkid;
        }

~'J'~

T.Willey

  • Needs a day job
  • Posts: 5251
Re: .NET BLOCK Routines
« Reply #2 on: January 28, 2010, 10:59:26 AM »
Here is one for inserting block within a layout, by layout name.  I would change it a little now, but a good example.

Code: [Select]
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;
using System.IO;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;

public static ObjectId InsertBlock ( Database db, string loName, string blkName, Point3d insPt ) {
    ObjectId RtnObjId = ObjectId.Null;
    using ( Transaction Trans = db.TransactionManager.StartTransaction( ) ) {
        DBDictionary LoDict = Trans.GetObject( db.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
        foreach ( DictionaryEntry de in LoDict ) {
            if ( string.Compare( ( string )de.Key, loName, true ).Equals( 0 ) ) {
                Layout Lo = Trans.GetObject( ( ObjectId )de.Value, OpenMode.ForWrite ) as Layout;
                BlockTable BlkTbl = Trans.GetObject( db.BlockTableId, OpenMode.ForRead ) as BlockTable;
                BlockTableRecord LoRec = Trans.GetObject( Lo.BlockTableRecordId, OpenMode.ForRead ) as BlockTableRecord;
                ObjectId BlkTblRecId = GetNonErasedTableRecordId( BlkTbl.Id, blkName );
                if ( BlkTblRecId.IsNull ) {
                    string BlkPath = HostApplicationServices.Current.FindFile( blkName + ".dwg", db, FindFileHint.Default );
                    if ( string.IsNullOrEmpty( BlkPath ) )
                        return RtnObjId;
                    BlkTbl.UpgradeOpen( );
                    using ( Database tempDb = new Database( false, true ) ) {
                        tempDb.ReadDwgFile( BlkPath, FileShare.Read, true, null );
                        db.Insert( blkName, tempDb, false );
                    }
                    BlkTblRecId = GetNonErasedTableRecordId( BlkTbl.Id, blkName );
                }
                LoRec.UpgradeOpen( );
                BlockReference BlkRef = new BlockReference( insPt, BlkTblRecId );
                LoRec.AppendEntity( BlkRef );
                Trans.AddNewlyCreatedDBObject( BlkRef, true );
                BlockTableRecord BlkTblRec = Trans.GetObject( BlkTblRecId, OpenMode.ForRead ) as BlockTableRecord;
                if ( BlkTblRec.HasAttributeDefinitions ) {
                    foreach ( ObjectId objId in BlkTblRec ) {
                        AttributeDefinition AttDef = Trans.GetObject( objId, OpenMode.ForRead ) as AttributeDefinition;
                        if ( AttDef != null ) {
                            AttributeReference AttRef = new AttributeReference( );
                            AttRef.SetAttributeFromBlock( AttDef, BlkRef.BlockTransform );
                            BlkRef.AttributeCollection.AppendAttribute( AttRef );
                            Trans.AddNewlyCreatedDBObject( AttRef, true );
                        }
                    }
                }
                Trans.Commit( );
            }
        }
    }
    return RtnObjId;
}

Thanks Oleg.  Forgot to add this sub by Tony T.
Code: [Select]
public static ObjectId GetNonErasedTableRecordId( ObjectId TableId, string Name )
    // Posted by Tony Tanzillo 01Sept2006
{
   ObjectId id = ObjectId.Null;
   using( Transaction tr = TableId.Database.TransactionManager.StartTransaction() )
   {
      SymbolTable table = (SymbolTable) tr.GetObject( TableId, OpenMode.ForRead );
      if( table.Has( Name ) )
      {
         id = table[Name];
         if( !id.IsErased )
            return id;
         foreach( ObjectId recId in table )
         {
            if( ! recId.IsErased )
            {
               SymbolTableRecord rec = (SymbolTableRecord) tr.GetObject( recId, OpenMode.ForRead );
               if( string.Compare( rec.Name, Name, true ) == 0 )
                  return recId;
            }
         }
      }
   }
   return id;
}
« Last Edit: January 28, 2010, 12:04:17 PM by T.Willey »
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

huiz

  • Swamp Rat
  • Posts: 917
  • Certified Prof C3D
Re: .NET BLOCK Routines
« Reply #3 on: August 15, 2010, 09:45:16 AM »
This code will place an existing block in ModelSpace, adds attributes and places it on the right layer. It also manages the current annotation scale if the block is annotative.
Thanks to fro2001 for this great piece of code!

Code: [Select]
  ' This procedure will insert a block into the current drawing. It also controls the defined attributes and it will
  ' add the current Annotation Scale if the block is Annotative.
  ' Code contribution:
  ' - fro2001 - via TheSwamp.org
  Public Sub hzInsertBlockMSWithAttributes(ByVal pntInsert As Geometry.Point3d,
                                           ByVal strBlockName As String,
                                           ByVal dScale As Double,
                                           ByVal strLayerName As String,
                                           ByVal arrAttrValues As ArrayList)

    Dim db As Database = HostApplicationServices.WorkingDatabase
    Using tr As Transaction = db.TransactionManager.StartTransaction
      Dim bt As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead)
      Dim btrMS As BlockTableRecord = bt(BlockTableRecord.ModelSpace).GetObject(OpenMode.ForRead)
      If Not bt.Has(strBlockName) Then
        Exit Sub
      Else
        btrMS.UpgradeOpen()
      End If
      Dim btr As BlockTableRecord = bt(strBlockName).GetObject(OpenMode.ForRead)
      Dim bref As New BlockReference(pntInsert, btr.ObjectId)
      btrMS.AppendEntity(bref)
      tr.AddNewlyCreatedDBObject(bref, True)
      ' set annotation scale if block is annotative
      If btr.Annotative = AnnotativeStates.True Then
        Dim ocm As ObjectContextManager = db.ObjectContextManager
        Dim occ As ObjectContextCollection = ocm.GetContextCollection("ACDB_ANNOTATIONSCALES")
        Internal.ObjectContexts.AddContext(bref, occ.CurrentContext)
      End If
      ' set attributes and values
      ' TODO: Set attribute to layer of block or layer of original definition, now it will be placed on layer 0
      ' TODO: Check if attribute is annotative, it is possible to create non-annotative blocks with annotative attributes
      Dim ccAttCounter As Integer = 0
      For Each objId As ObjectId In btr
        Dim ent As Entity = objId.GetObject(OpenMode.ForRead)
        If TypeOf ent Is AttributeDefinition Then
          ent.UpgradeOpen()
          Dim attDef As AttributeDefinition = CType(ent, AttributeDefinition)
          Dim attRef As New AttributeReference
          attRef.SetAttributeFromBlock(attDef, bref.BlockTransform)
          ' check if there is a value to add
          If arrAttrValues.Count - 1 >= ccAttCounter Then
            attRef.TextString = arrAttrValues(ccAttCounter).ToString.Trim
          Else
            attRef.TextString = ""
          End If
          bref.AttributeCollection.AppendAttribute(attRef)
          tr.AddNewlyCreatedDBObject(attRef, True)
          ccAttCounter += 1
        End If
      Next
      ' set layer
      ' TODO: Check if layername exist
      bref.Layer = strLayerName
      ' commit
      tr.Commit()
    End Using
  End Sub

The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #4 on: November 23, 2010, 05:43:48 PM »
Hi,
Trying to help someone on http://www.acadnetwork.com/, who wanted to delete duplicated block references.
Are considered as duplicated blocks, those which have the same name, layer, insertion point, rotation and scales.

I first wrote a C# routine using a 'Duplicate' extension method and I thaught it was a good exercice to improve my very new F# learning.
Here're both implementations.

Code: [Select]
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace DuplicateTest
{
    public static class BlockRefExtension
    {
        public static bool Duplicate(this BlockReference blk1, BlockReference blk2)
        {
            Tolerance tol = new Tolerance(1e-6, 1e-6);
            return
                blk1.OwnerId == blk2.OwnerId &&
                blk1.Name == blk2.Name &&
                blk1.Layer == blk2.Layer &&
                Math.Round(blk1.Rotation, 5) == Math.Round(blk2.Rotation, 5) &&
                blk1.Position.IsEqualTo(blk2.Position, tol) &&
                blk1.ScaleFactors.IsEqualTo(blk2.ScaleFactors, tol);
        }
    }

    public class Commands
    {
        [CommandMethod("Test")]
        public void DelDupBlkRef()
        {
            try
            {
                int del = DeleteDuplicatedBlockRef();
                Application.ShowAlertDialog(del.ToString() + " duplicated block(s) have been erased");
            }
            catch (System.Exception e)
            {
                Application.ShowAlertDialog("\nError: " + e.Message);
            }
        }

        private int DeleteDuplicatedBlockRef()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            int result = 0;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId id in bt)
                {
                    BlockTableRecord btr =
                        (BlockTableRecord)tr.GetObject(id, OpenMode.ForWrite);
                    ObjectIdCollection idCol = btr.GetBlockReferenceIds(true, false);
                    for (int i = 0; i < idCol.Count; i++)
                    {
                        BlockReference blkRef =
                            (BlockReference)tr.GetObject(idCol[i], OpenMode.ForRead);
                        for (int j = i + 1; j < idCol.Count; j++)
                        {
                            BlockReference br =
                                (BlockReference)tr.GetObject(idCol[j], OpenMode.ForRead);
                            if (br.Duplicate(blkRef))
                            {
                                idCol.RemoveAt(j);
                                br.UpgradeOpen();
                                br.Erase();
                                j--;
                                result++;
                            }
                        }
                    }
                }
                tr.Commit();
            }
            return result;
        }
    }
}

Code: [Select]
module DupBlks

open System
open System.Collections.Generic
open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime

let Duplicate (a: BlockReference) (b: BlockReference) =
    let tol = new Tolerance(1e-6, 1e-6)
    a.Name = b.Name &&
    a.OwnerId = b.OwnerId &&
    a.Position = b.Position &&
    a.Layer = b.Layer &&
    Math.Round(a.Rotation, 6) = Math.Round(b.Rotation, 6) &&
    a.ScaleFactors = b.ScaleFactors

let DeleteDuplicatedBlockRef() =
    let db = HostApplicationServices.WorkingDatabase
    use tr = db.TransactionManager.StartTransaction()
    
    let getRefs (bt: BlockTable) =
        let mutable lst = []
        for id in bt do
            let btr = tr.GetObject(id, OpenMode.ForRead) :?> BlockTableRecord
            for oId in btr.GetBlockReferenceIds(true, false) do
                lst <- tr.GetObject(oId, OpenMode.ForWrite) :?> BlockReference :: lst
        lst

    let rec findDups acc blks =
        match blks with
        | [] -> acc
        | h :: t -> let pair = List.partition (fun x -> Duplicate x h) t
                    findDups (acc @ (fst pair)) (snd pair)
    let delBlk lst =
        List.iter (fun (x: BlockReference) -> x.Erase()) lst
        lst.Length
                
    let cnt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) :?> BlockTable
              |> getRefs |> findDups [] |> delBlk
    tr.Commit()
    cnt
        
[<CommandMethod("Test")>]
let Test() =
    try
        let del = DeleteDuplicatedBlockRef()
        Application.ShowAlertDialog(del.ToString() + " duplicated block(s) have been erased")
    with
        |ex -> Application.ShowAlertDialog(ex.Message)

« Last Edit: November 23, 2010, 06:16:00 PM by gile »
Speaking English as a French Frog

Jeff H

  • Needs a day job
  • Posts: 6150
Re: .NET BLOCK Routines
« Reply #5 on: December 10, 2010, 05:26:13 AM »
A little simple one that will change the Visibilty parameter of a all blockreferences based from a dynamic block

Maybe you could add another for each loop in this one



Code: [Select]
[CommandMethod("DynamicBlocksThingy")]
        public void DynamicBlocksThingy()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            using (Transaction trx = db.TransactionManager.StartTransaction())
            {
               
                BlockTable bt = db.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                foreach (ObjectId objId in bt)
                {
                    BlockTableRecord btr = (BlockTableRecord)objId.GetObject(OpenMode.ForRead);

                    if (btr.IsDynamicBlock)
                    {
                        ObjectIdCollection brefObjidColl = btr.GetBlockReferenceIds(true, true);
                        ObjectIdCollection anonObjIdsColl = btr.GetAnonymousBlockIds();

                        foreach (ObjectId anonObjId in anonObjIdsColl)
                        {
                            BlockTableRecord btrAnon = (BlockTableRecord)anonObjId.GetObject(OpenMode.ForRead);
                            ObjectIdCollection ObjidColl = btrAnon.GetBlockReferenceIds(true, true);
                            foreach (ObjectId Id in ObjidColl)
                            brefObjidColl.Add(Id);
                        }

                        foreach (ObjectId brefObjId in brefObjidColl)
                        {
                            BlockReference bref = brefObjId.GetObject(OpenMode.ForWrite) as BlockReference;
                            DynamicBlockReferencePropertyCollection dynBrefColl = bref.DynamicBlockReferencePropertyCollection;
                            foreach (DynamicBlockReferenceProperty dynBrefProps in dynBrefColl)
                            {
                                if (dynBrefProps.PropertyName == "Visibility1")
                                {
                                    dynBrefProps.Value = "Vis2";
                                }
                            }
                        }
                    }
                }
                               
               
                trx.Commit();
            }
        }

mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
Get Dynamic Blocks by Name
« Reply #6 on: February 25, 2011, 02:05:39 PM »
This function will get all dynamic blocks by name.
Normally this would be a simple filter but dynamic blocks become anonymous as soon as a dynamic property is changed.
Code: [Select]
        public static ObjectIdCollection GetDynamicBlocksByName(string BlkName)
        {
            ObjectIdCollection res = new ObjectIdCollection();
            Database db = Application.DocumentManager.MdiActiveDocument.Database;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                //get the blockTable and iterate through all block Defs
                BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId btrId in bt)
                {
                    //get the block Def and see if it is anonymous
                    BlockTableRecord btr = (BlockTableRecord)trans.GetObject(btrId, OpenMode.ForRead);
                    if (btr.IsDynamicBlock && btr.Name.Equals(BlkName))
                    {
                        //get all anonymous blocks from this dynamic block
                        ObjectIdCollection anonymousIds = btr.GetAnonymousBlockIds();
                        ObjectIdCollection dynBlockRefs = new ObjectIdCollection();
                        foreach (ObjectId anonymousBtrId in anonymousIds)
                        {
                            //get the anonymous block
                            BlockTableRecord anonymousBtr = (BlockTableRecord)trans.GetObject(anonymousBtrId, OpenMode.ForRead);
                            //and all references to this block
                            ObjectIdCollection blockRefIds = anonymousBtr.GetBlockReferenceIds(true, true);
                            foreach (ObjectId id in blockRefIds) dynBlockRefs.Add(id);
                        }
                        res = dynBlockRefs;
                        break;
                    }
                }
                trans.Commit();
            }
            return res;
        }
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #7 on: November 05, 2011, 08:12:21 AM »
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).

The extraction is collected in a System.Data.DataTable so that it can be easily converted to an AutoCAD table, an Exel sheet and so on.
The DataTable.WriteXls() extension method uses late binding while interacting with Excel application to avoid version reference dependency.

The BlockAttribute class which defines a new Type which main properties are:
- Block:  the block (effective) name ;
- Attributes: a dictionary for the block attributes where Key is the tag and Value the text string.
The BlockAttributeEqualityComparer class implements the IEqualityComparer<BlockAttribute> interface so that BlockAttribute collections can be used with some Linq extension methods as GroupBy().
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace AttributeExtraction
{
    public class BlockAttribute
    {
        private string _name;
        private Dictionary<string, string> _atts;

        // Public read only properties
        public string Name
        {
            get { return _name; }
        }

        public Dictionary<string, string> Attributes
        {
            get { return _atts; }
        }

        public string this[string key]
        {
            get { return _atts[key.ToUpper()]; }
        }

        // Constructors
        public BlockAttribute(BlockReference br)
        {
            SetProperties(br);
        }

        public BlockAttribute(ObjectId id)
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            using (Transaction tr = doc.TransactionManager.StartTransaction())
            {
                SetProperties(tr.GetObject(id, OpenMode.ForRead) as BlockReference);
            }
        }

        // Public method
        new public string ToString()
        {
            if (_atts != null && _atts.Count > 0)
                return string.Format("{0}: {1}",
                    _name,
                    _atts.Select(a => string.Format("{0}={1}", a.Key, a.Value))
                        .Aggregate((a, b) => string.Format("{0}; {1}", a, b)));
            return _name;
        }

        // Private method
        private void SetProperties(BlockReference br)
        {
            if (br == null) return;
            _name = br.GetEffectiveName();
            _atts = new Dictionary<string, string>();
            br.AttributeCollection
                .GetObjects<AttributeReference>()
                .Iterate(att => _atts.Add(att.Tag.ToUpper(), att.TextString));
        }
    }

    public class BlockAttributeEqualityComparer : IEqualityComparer<BlockAttribute>
    {
        public bool Equals(BlockAttribute x, BlockAttribute y)
        {
            return
                x.Name.Equals(y.Name, StringComparison.CurrentCultureIgnoreCase) &&
                x.Attributes.SequenceEqual(y.Attributes);
        }

        public int GetHashCode(BlockAttribute obj)
        {
            return base.GetHashCode();
        }
    }
}

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;
        }
    }
}

The LateBinding class provides helpers to write late binding instructions in a more friendly style (thanks to Thorsten and Tony T).
Code: [Select]
using BF = System.Reflection.BindingFlags;

namespace AttributeExtraction
{
    public static class LateBinding
    {
        public static object GetInstance(string appName)
        {
            return System.Runtime.InteropServices.Marshal.GetActiveObject(appName);
        }

        public static object CreateInstance(string appName)
        {
            return System.Activator.CreateInstance(System.Type.GetTypeFromProgID(appName));
        }

        public static object GetOrCreateInstance(string appName)
        {
            try { return GetInstance(appName); }
            catch { return CreateInstance(appName); }
        }

        public static object Get(this object obj, string propName, params object[] parameter)
        {
            return obj.GetType().InvokeMember(propName, BF.GetProperty, null, obj, parameter);
        }

        public static void Set(this object obj, string propName, params object[] parameter)
        {
            obj.GetType().InvokeMember(propName, BF.SetProperty, null, obj, parameter);
        }

        public static object Invoke(this object obj, string methName, params object[] parameter)
        {
            return obj.GetType().InvokeMember(methName, BF.InvokeMethod, null, obj, parameter);
        }

        public static void ReleaseInstance(this object obj)
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        }
    }
}

A testing command
Code: [Select]
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace AttributeExtraction
{
    public class Commands
    {
        [CommandMethod("Test")]
        public void Test()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            TypedValue[] filter = { new TypedValue(0, "INSERT") };
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            if (psr.Status != PromptStatus.OK) return;
            PromptPointResult ppr = ed.GetPoint("\nInsertion point: ");
            if (ppr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                System.Data.DataTable dataTable = psr.Value.GetObjectIds()
                    .Select(id => new BlockAttribute(id.GetObject<BlockReference>()))
                    .ToDataTable("Extraction");
                Table tbl = dataTable.ToAcadTable(9.0, 40.0);
                tbl.Position = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
                BlockTableRecord btr = db.CurrentSpaceId.GetObject<BlockTableRecord>(OpenMode.ForWrite);
                btr.AppendEntity(tbl);
                tr.AddNewlyCreatedDBObject(tbl, true);
                try
                {
                    string filename = (string)AcAp.GetSystemVariable("dwgprefix") + "Extraction.xls";
                    dataTable.WriteXls(filename, null, true);
                }
                catch
                {
                    AcAp.ShowAlertDialog("Failed to open Excel");
                }
                tr.Commit();
            }
        }
    }
}
« Last Edit: November 06, 2011, 04:56:27 AM by gile »
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #8 on: November 06, 2011, 02:53:55 AM »
I added some methods in the upper example: System.Data.Datatable.WriteCsv (to write the datatable in a CSV file) and System.Data.DataTable.WriteXls (to write the datatable in an Excel sheet) which uses late binding to avoid Excel version dependency. The LateBinding class provises methods to make late binding instruction more readable (thanks to Thorsten and Tony T).
« Last Edit: November 06, 2011, 03:04:59 AM by gile »
Speaking English as a French Frog

kaefer

  • Guest
Re: .NET BLOCK Routines
« Reply #9 on: November 08, 2011, 09:22:25 AM »
Here's a little example for attributes extraction.

Awesome, that's what your little example is: Just awesome.

Here's a rudimentary F# version doing the same; it reads attributes from the block references in a selection set, counts the different attribute sets, stores this information in a DataTable and finally converts that to an AutoCAD table.

The aim is not to improve on gile's original creation (that's hardly possible) but to illustrate the paradigmatic differences in implementation.

- let-bound functions instead of extension methods
- pipelining (|>) and function composition (>>) instead of method chaining
- reverse argument order compared to extension methods to support pipelining
- uses a data type which supports, inter alia, IEquatable and IStructuralEquatable natively, obviating the need for overriden Equals and GetHashCode methods
- tries to be more imperative "in the small" because for foo in bar do ... reads nicer than bar |> Seq.iter (fun foo -> ...)

Code: [Select]
// Original written in C# by gile @ theswamp
// http://www.theswamp.org/index.php?topic=31859.msg452527#msg452527

module AttributeExtraction

open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime
open System.Data

type acadApp = Autodesk.AutoCAD.ApplicationServices.Application

// type test returning 'T option
let ofType<'T> : obj -> _ =
    box
    >> function :? 'T as t -> Some t | _ -> None

// get DBObject for read and cast to 'T, if type parameter present
let getObject<'T when 'T :> DBObject> (oid: ObjectId) =
    oid.GetObject OpenMode.ForRead
    |> unbox<'T>

// get all objects from an untyped sequence, and try to cast them to 'T
let getObjects<'T when 'T :> DBObject> : System.Collections.IEnumerable -> _ =
    Seq.cast<ObjectId>
    >> Seq.choose (getObject >> ofType<'T>)

// F# record type to encapsulate a block reference and its attributes
type BlockAttribute =
    {   
        Name : string
        Attributes : Map<string,string>
    } with
    // indexer method
    member me.Item (key : string) = me.Attributes.[key.ToUpper()]
   
    override me.ToString() =
        if me.Attributes.IsEmpty then
            me.Name
        else
            let sb = new System.Text.StringBuilder()
            me.Attributes
            |> Map.fold (fun first k v ->
                if first then
                    sb.AppendFormat("{0}: {1}={2}", me.Name, k, v)
                else
                    sb.AppendFormat("; {0}={1}", k, v)
                |> ignore
                false ) true |> ignore
            sb.ToString()
   
    // Ersatz constructors
    static member Create oid = BlockAttribute.Create (getObject oid)
    static member Create(br : BlockReference) =
        {   Name =
                // Gets the block effective name (anonymous dynamic blocs)
                if br.IsDynamicBlock then
                    (getObject<BlockTableRecord> br.DynamicBlockTableRecord).Name
                else
                    br.Name
            Attributes =
                getObjects<AttributeReference> br.AttributeCollection
                |> Seq.map (fun ar -> ar.Tag.ToUpper(), ar.TextString)
                |> Map.ofSeq }
   
// Create a Datatable from a BlockAttribute collection
let toDataTable name blockAtts =
    let dTable = new System.Data.DataTable(name)
    dTable.Columns.Add("Name", typeof<string>)  |> ignore
    dTable.Columns.Add("Quantity", typeof<int>) |> ignore
    blockAtts
    |> Seq.groupBy id
    |> Seq.map (fun (blk, blks) -> blk, Seq.length blks)
    |> Seq.iter (fun (blk, bcnt) ->
        let dRow = dTable.Rows.Add(blk.Name, bcnt)
        for KeyValue(k, v) in blk.Attributes do
            if not(dTable.Columns.Contains k) then
                dTable.Columns.Add k |> ignore
            dRow.[k] <- v )
    dTable

// Create an AutoCAD Table from a Datatable
let toAcadTable rowHeight columnWidth (dTable : System.Data.DataTable) =
    let tbl = new Table()
    tbl.Rows.[0].Height <- rowHeight
    tbl.Columns.[0].Width <- columnWidth
    tbl.InsertColumns(0, columnWidth, dTable.Columns.Count - 1)
    tbl.InsertRows(0, rowHeight, dTable.Rows.Count + 1)
    tbl.Cells.[0, 0].Value <- dTable.TableName
    for x in 0 .. dTable.Columns.Count - 1 do
        let col = dTable.Columns.[x]
        tbl.Cells.[1, x].Value <- col.ColumnName
        for y in 0 .. dTable.Rows.Count - 1 do
            tbl.Cells.[y + 2, x].Value <- dTable.Rows.[y].[col]
    tbl

// Test command
[<CommandMethod("TestF", CommandFlags.UsePickSet)>]
let testCommand() =
    let doc = acadApp.DocumentManager.MdiActiveDocument
    let db = doc.Database
    let ed = doc.Editor
    let filter = [| new TypedValue(0, "INSERT") |]
    let psr = ed.GetSelection(new SelectionFilter(filter))
    if psr.Status = PromptStatus.OK then
        let ppr = ed.GetPoint "\nInsertion point: "
        if ppr.Status = PromptStatus.OK then
            use tr = db.TransactionManager.StartTransaction()
            let tbl =
                psr.Value.GetObjectIds()
                |> Seq.map BlockAttribute.Create
                |> toDataTable "Extraction"
                |> toAcadTable 9.0 40.0
            tbl.Position <- ppr.Value.TransformBy ed.CurrentUserCoordinateSystem
            let btr : BlockTableRecord = getObject db.CurrentSpaceId
            btr.UpgradeOpen()
            btr.AppendEntity tbl |> ignore
            tr.AddNewlyCreatedDBObject(tbl, true)
            tr.Commit()

Edited: getObjects needs a type annotation to be generic
« Last Edit: November 08, 2011, 03:21:03 PM by kaefer »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #10 on: November 08, 2011, 11:13:15 AM »
Thanks Thorsten.
I learn(ed) a lot from you codes and really like very much your GetObject<T>() and GetObjects<T>() methods.
The upper F# code is very nice.

Quote
- tries to be more imperative "in the small" because for foo in bar do ... reads nicer than bar |> Seq.iter (fun foo -> ...)
This is funny, while I try to write C# in a more functional/declarative style, you write F# in a more imperative one.
Speaking English as a French Frog

fixo

  • Guest
Re: .NET BLOCK Routines
« Reply #11 on: January 08, 2012, 01:00:05 PM »
Very basic code snip for appending attribute to block
Not tested on multiline attributes
Code: [Select]

      [CommandMethod("AppendAttributeToBlock","appatt", CommandFlags.Session | CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
        public static void AppendAttributeTest()
           {         
            Document doc = acadApp.DocumentManager.MdiActiveDocument;

            Editor ed = doc.Editor;

            Database db = doc.Database;

            try
            {
                using (doc.LockDocument())
                {
                    using (Transaction tr = db.TransactionManager.StartTransaction())
                    {
                        BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;

                        BlockTableRecord currSp = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                       
                        PromptNestedEntityOptions pno =

                         new PromptNestedEntityOptions("\nSelect source attribute to append new attribute below this one >>");

                        PromptNestedEntityResult nres =

                                                ed.GetNestedEntity(pno);

                        if (nres.Status != PromptStatus.OK)

                            return;

                        ObjectId id = nres.ObjectId;

                        Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);

                        Point3d pnt = nres.PickedPoint;

                        ObjectId owId = ent.OwnerId;

                        AttributeReference attref = null;

                        if (id.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(AttributeReference))))
                        {
                            attref = tr.GetObject(id,OpenMode.ForWrite) as AttributeReference;
                        }
                     

                        BlockTableRecord btr = null;

                        BlockReference bref = null;

                        if (owId.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(BlockReference))))
                        {
                            bref = tr.GetObject(owId,OpenMode.ForWrite) as BlockReference;

                            if (bref.IsDynamicBlock)
                            {
                                btr = tr.GetObject(bref.DynamicBlockTableRecord,OpenMode.ForRead) as BlockTableRecord;
                            }
                            else
                            {
                                btr = tr.GetObject(bref.BlockTableRecord,OpenMode.ForRead) as BlockTableRecord;
                            }
                        }
                       
                        Point3d insPt = attref.Position.TransformBy(bref.BlockTransform);

                        btr.UpgradeOpen();

                        ObjectIdCollection bids = new ObjectIdCollection();

                        AttributeDefinition def = null;

                        foreach (ObjectId defid in btr)
                        {
                            if (defid.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(AttributeDefinition))))
                            {
                                def = tr.GetObject(defid, OpenMode.ForRead) as AttributeDefinition;

                                if (def.Tag == attref.Tag)
                                {
                                    def.UpgradeOpen();

                                    bids.Add(defid);

                                    break;
                                }
                            }
                        }

                     

                        IdMapping map = new IdMapping();

                        db.DeepCloneObjects(bids, btr.ObjectId, map,true);

                        ObjectIdCollection coll = new ObjectIdCollection();

                        AttributeDefinition attDef = null;

                        foreach (IdPair pair in map)
                        {
                            if (pair.IsPrimary)
                            {
                                Entity oent = (Entity)tr.GetObject(pair.Value, OpenMode.ForWrite);

                                if (oent != null)
                                {

                                    if (pair.Value.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(AttributeDefinition))))
                                    {
                                         attDef = oent as AttributeDefinition;

                                        attDef.UpgradeOpen();

                                        attDef.SetPropertiesFrom(def as Entity);
                                        // add other properties from source attribute definition to suit here:

                                        attDef.Justify = def.Justify;

                                        attDef.Position = btr.Origin.Add(

                                            new Vector3d(attDef.Position.X, attDef.Position.Y - attDef.Height * 1.25, attDef.Position.Z)).TransformBy(Matrix3d.Identity

                                            );

                                        attDef.Tag = "NEW_TAG";

                                        attDef.TextString = "New Prompt";

                                        attDef.TextString = "New Textstring";

                                        coll.Add(oent.ObjectId);


                                    }
                                }
                            }
                        }
                        btr.AssumeOwnershipOf(coll);

                        btr.DowngradeOpen();

                        attDef.Dispose();//optional

                        bref.RecordGraphicsModified(true);

                        tr.TransactionManager.QueueForGraphicsFlush();

                        doc.TransactionManager.FlushGraphics();//optional

                        ed.UpdateScreen();

                        tr.Commit();
                    }

                }
            }
               
            catch (System.Exception ex)
            {
                ed.WriteMessage(ex.Message + "\n" + ex.StackTrace);
            }
            finally
            {
                acadApp.ShowAlertDialog("Call command \"ATTSYNC\" manually");

            }

        }

Benzirpi

  • Guest
Re: .NET BLOCK Routines
« Reply #12 on: February 01, 2012, 08:53:26 AM »
Here's a little example about inserting a block (a .dwg file) into the current open drawing. It uses EntitiyJig in order to ask dinamically the user to pick a insertion point and set a rotation angle.

The main function:
Code: [Select]
    '/**
    '* Pedir al usuario donde y con que angulo, para insertar el bloque indicado en el plano abierto en ese momento.
    '*/
    Public Shared Sub InsertarBloque(ByVal archivoDWG As String)
        Dim acadDoc As Document
        acadDoc = Application.DocumentManager.MdiActiveDocument
        Using acadLockDoc As DocumentLock = acadDoc.LockDocument()

            Dim dbTemporal As Database
            dbTemporal = New Database(buildDefaultDrawing:=False, noDocument:=True)
            dbTemporal.ReadDwgFile(fileName:=archivoDWG, _
                                    fileSharing:=IO.FileShare.Read, _
                                    allowCPConversion:=False, _
                                    password:="")
            Dim nombreBloque As String
            nombreBloque = IO.Path.GetFileNameWithoutExtension(archivoDWG)
            SymbolUtilityServices.ValidateSymbolName(name:=nombreBloque, allowVerticalBar:=False)

            Dim acadDB As Database
            acadDB = acadDoc.Database
            Dim idBloque As ObjectId
            idBloque = acadDB.Insert(blockName:=nombreBloque, dataBase:=dbTemporal, preserveSourceDatabase:=False)
            Dim bloqueAInsertar As BlockReference
            bloqueAInsertar = New BlockReference(position:=Point3d.Origin, BlockTableRecord:=idBloque)

            Dim acadEditor As Editor
            acadEditor = acadDoc.Editor
            bloqueAInsertar.TransformBy(acadEditor.CurrentUserCoordinateSystem)

            Dim unaPlantilla As PlantillaBloque_PedirPuntoDeInserccion
            unaPlantilla = New PlantillaBloque_PedirPuntoDeInserccion(bloqueAInsertar)
            Dim resultadoPedirPunto As PromptResult
            resultadoPedirPunto = acadEditor.Drag(unaPlantilla)
            If resultadoPedirPunto.Status.Equals(PromptStatus.OK) Then

                Dim otraPlantilla As PlantillaBloque_PedirAnguloDeRotacion
                otraPlantilla = New PlantillaBloque_PedirAnguloDeRotacion(bloqueAInsertar, _
                                                                          anguloBase:=bloqueAInsertar.Rotation, _
                                                                          sistemaCoordenadas:=acadEditor.CurrentUserCoordinateSystem.CoordinateSystem3d, _
                                                                          puntoBase:=unaPlantilla.getPuntoDeInsercion)
                Dim resultadoPedirAngulo As PromptResult
                resultadoPedirAngulo = acadEditor.Drag(otraPlantilla)
                If resultadoPedirAngulo.Status.Equals(PromptStatus.OK) Then
                    otraPlantilla.ActualizarRotacion()
                Else
                    bloqueAInsertar.Rotation = 0.0
                End If

                Using acadTrans As Transaction = acadDoc.TransactionManager.StartTransaction
                    Dim espacioActual As BlockTableRecord
                    espacioActual = CType(acadTrans.GetObject(acadDoc.Database.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord)
                    espacioActual.AppendEntity(entity:=bloqueAInsertar)
                    acadTrans.AddNewlyCreatedDBObject(obj:=bloqueAInsertar, add:=True)

                    Dim bloque As BlockTableRecord
                    bloque = CType(acadTrans.GetObject(idBloque, OpenMode.ForWrite), BlockTableRecord)
                    If bloque.HasAttributeDefinitions Then
                        Dim idElemento As ObjectId
                        For Each idElemento In bloque
                            Dim elemento As DBObject
                            elemento = acadTrans.GetObject(idElemento, OpenMode.ForRead)
                            If elemento.GetType.Name.Equals("AttributeDefinition") Then
                                Dim definicionDeAtributo As AttributeDefinition
                                definicionDeAtributo = CType(acadTrans.GetObject(idElemento, OpenMode.ForRead), AttributeDefinition)
                                Dim referenciaAAtributo As AttributeReference
                                referenciaAAtributo = New AttributeReference
                                referenciaAAtributo.SetAttributeFromBlock(definicionDeAtributo, bloqueAInsertar.BlockTransform)
                                bloqueAInsertar.AttributeCollection.AppendAttribute(referenciaAAtributo)
                                acadTrans.AddNewlyCreatedDBObject(obj:=referenciaAAtributo, add:=True)
                            End If
                        Next
                   End If
                   acadTrans.Commit()
                End Using '//acadTrans
            End If

        End Using '//acadLockDoc

        Application.UpdateScreen()

    End Sub


Ask user for insertion point:
Code: [Select]
Friend Class PlantillaBloque_PedirPuntoDeInserccion
Inherits Autodesk.Autocad.EditorInput.EntityJig

    Dim mPuntoDeInsercion, mPuntoActivo As Point3d


    Public Sub New(bloqueAInsertar as BlockReference)
        MyBase.New(bloqueAInsertar)
        mPuntoDeInsercion = bloqueAInsertar.Position
    End Sub

    Protected Overrides Function Sampler(ByVal prompts As Autodesk.AutoCAD.EditorInput.JigPrompts) As Autodesk.AutoCAD.EditorInput.SamplerStatus
        Dim opciones As New JigPromptPointOptions
        opciones.UserInputControls = UserInputControls.Accept3dCoordinates or _
                                     UserInputControls.NoZeroResponseAccepted or _
                                     UserInputControls.NoNegativeResponseAccepted
        opciones.Message = environment.NewLine & "Punto de insercion: "
        Dim resultado As PromptPointResult
        resultado = prompts.AcquirePoint(opciones)
        If mPuntoActivo = resultado.Value Then
            Return SamplerStatus.NoChange
        Else
            mPuntoActivo = resultado.Value
            Return SamplerStatus.OK
        End If
    End Function

    Protected Overrides Function Update() As Boolean
        mPuntoDeInsercion = mPuntoActivo
        Try
            CType(Entity, BlockReference).Position = mPuntoDeInsercion
        Catch ex As Exception
            Return False
        End Try
        Return True
    End Function


    Public Function getEntity As Entity
        Return Entity
    End Function


    Public Function getPuntoDeInsercion As Point3d
        Return mPuntoDeInsercion
    End Function

   
End Class



Ask user for rotation angle:
Code: [Select]
Friend Class PlantillaBloque_PedirAnguloDeRotacion
Inherits Autodesk.Autocad.EditorInput.EntityJig

    Dim mEntidadARotar As BlockReference
    Dim mAnguloBase, mVariacionAngulo As Double
    Dim mSistemaDeCoordenadas As CoordinateSystem3d
    Dim mPuntoBase As Point3d
    Dim mResultado As string


    Public Sub New(bloqueAInsertar as BlockReference, _
                   anguloBase As Double, _
                   sistemaCoordenadas As CoordinateSystem3d, _
                   puntoBase As Point3d)
        MyBase.New(bloqueAInsertar)
        mEntidadARotar = bloqueAInsertar
        mAnguloBase = anguloBase
        mSistemaDeCoordenadas = sistemaCoordenadas
        mPuntoBase = puntoBase
    End Sub

    Protected Overrides Function Sampler(ByVal prompts As Autodesk.AutoCAD.EditorInput.JigPrompts) As Autodesk.AutoCAD.EditorInput.SamplerStatus
        Dim opciones As New JigPromptAngleOptions
        opciones.Message = environment.NewLine & "Angulo a rotar: "
        opciones.DefaultValue = 0
        opciones.BasePoint = mPuntoBase
        opciones.UseBasePoint = True
        opciones.UserInputControls=UserInputControls.Accept3dCoordinates or UserInputControls.NullResponseAccepted
        Dim resultado As PromptDoubleResult
        resultado = prompts.AcquireAngle(opciones)
        If resultado.Status = PromptStatus.OK Then
            mResultado = resultado.StringResult
            If mVariacionAngulo = resultado.Value Then
                Return SamplerStatus.NoChange
            Else
                mVariacionAngulo = resultado.Value
                Return SamplerStatus.OK
            End If
        End If
        Return SamplerStatus.Cancel
    End Function

    Protected Overrides Function Update() As Boolean
        mEntidadARotar.Rotation = mVariacionAngulo + mAnguloBase
        Return True
    End Function

    Public Sub ActualizarRotacion
        mEntidadARotar.Rotation = mVariacionAngulo + mAnguloBase
    End Sub


    Public Function getEntity As Entity
        Return Entity
    End Function

    Public Function getAnguloDeRotacion As Double
        Return mAnguloBase
    End Function

    Public Function getResultado As String
        Return mResultado
    End Function
   
End Class


« Last Edit: January 08, 2014, 12:40:31 PM by Benzirpi »

trembre

  • Guest
Re: .NET BLOCK Routines
« Reply #13 on: February 01, 2012, 06:41:06 PM »
This function returns an ObjectIdCollection of all instances of a particular block reference by name in modelspace;

 
Code: [Select]
public ObjectIdCollection GetModelSpaceBlockReferenceOIDCByBlockName(string blockReferenceName)
        {
            ObjectIdCollection returnOIDs = new ObjectIdCollection();

            using (Database db = AcadApp.DocumentManager.MdiActiveDocument.Database)
            {
                using (Transaction trans = db.TransactionManager.StartTransaction())
                {
                    BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                    BlockTableRecord btrModel = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
                    foreach (ObjectId thisOid in btrModel)
                    {
                        Entity blk = (Entity)trans.GetObject(thisOid, OpenMode.ForRead);
                        if (blk.GetType().ToString() == "Autodesk.AutoCAD.DatabaseServices.BlockReference")
                        {
                            BlockReference thisBlockRef = (BlockReference)trans.GetObject(thisOid, OpenMode.ForRead);
                            if(string.Compare(thisBlockRef.Name, blockReferenceName, true) == 0)
                            {
                                returnOIDs.Add(thisOid);
                            }
                        }
                    }
                    return returnOIDs;
                }
            }
        }

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #14 on: January 27, 2013, 01:29:33 PM »
Dragging and rotating an (attributed) block.

BlockJig is to be used to drag (and possibly rotate) a simple block.
While dragging the block, it's possible to dynamically rotate the block by pressing Ctrl.
A click terminate the process keeping the current position and rotation for the block

BlockAttribJig inherits from BlockJig and handles the block attribute references.
In the constructor, each attribute definition informations (position, alignement and rotation) is stored in a structure.
The overrided Update() method sets the attributes geometry according to the attribute definition informations and the block reference position and rotation.

Code - C#: [Select]
  1.         class BlockJig : EntityJig
  2.         {
  3.             protected BlockReference _br;
  4.             protected Point3d _pos;
  5.             protected double _rot, _ucsRot;
  6.  
  7.             public BlockJig(BlockReference br)
  8.                 : base(br)
  9.             {
  10.                 _br = br;
  11.                 _pos = _br.Position;
  12.                 Editor ed = AcAp.DocumentManager.MdiActiveDocument.Editor;
  13.                 CoordinateSystem3d ucs = ed.CurrentUserCoordinateSystem.CoordinateSystem3d;
  14.                 Matrix3d ocsMat = Matrix3d.WorldToPlane(new Plane(Point3d.Origin, ucs.Zaxis));
  15.                 _ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis);
  16.                 _rot = _br.Rotation - _ucsRot;
  17.             }
  18.  
  19.             protected override SamplerStatus Sampler(JigPrompts prompts)
  20.             {
  21.                 System.Windows.Forms.Keys mods = System.Windows.Forms.Control.ModifierKeys;
  22.                 if ((mods & System.Windows.Forms.Keys.Control) > 0)
  23.                 {
  24.                     JigPromptAngleOptions jpao =
  25.                         new JigPromptAngleOptions("\nSpecify the rotation: ");
  26.                     jpao.UseBasePoint = true;
  27.                     jpao.BasePoint = _br.Position;
  28.                     jpao.Cursor = CursorType.RubberBand;
  29.                     jpao.UserInputControls = (
  30.                         UserInputControls.Accept3dCoordinates |
  31.                         UserInputControls.UseBasePointElevation);
  32.                     PromptDoubleResult pdr = prompts.AcquireAngle(jpao);
  33.  
  34.                     if (_rot == pdr.Value)
  35.                     {
  36.                         return SamplerStatus.NoChange;
  37.                     }
  38.                     else
  39.                     {
  40.                         _rot = pdr.Value;
  41.                         return SamplerStatus.OK;
  42.                     }
  43.                 }
  44.                 else
  45.                 {
  46.                     JigPromptPointOptions jppo =
  47.                         new JigPromptPointOptions("\nSpecify insertion point (or press Ctrl for rotation): ");
  48.                     jppo.UserInputControls =
  49.                       (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted);
  50.                     PromptPointResult ppr = prompts.AcquirePoint(jppo);
  51.                     if (_pos.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint)
  52.                     {
  53.                         return SamplerStatus.NoChange;
  54.                     }
  55.                     else
  56.                     {
  57.                         _pos = ppr.Value;
  58.                     }
  59.                     return SamplerStatus.OK;
  60.                 }
  61.             }
  62.  
  63.             protected override bool Update()
  64.             {
  65.                 _br.Position = _pos;
  66.                 _br.Rotation = _rot +_ucsRot;
  67.                 return true;
  68.             }
  69.         }
  70.  
  71.         class BlockAttribJig : BlockJig
  72.         {
  73.             struct TextInfo
  74.             {
  75.                 public Point3d Position;
  76.                 public Point3d Alignment;
  77.                 public double Rotation;
  78.                 public bool IsAligned;
  79.             }
  80.  
  81.             private Dictionary<string, TextInfo> _attInfos;
  82.  
  83.             public BlockAttribJig(BlockReference br)
  84.                 : base(br)
  85.             {
  86.                 _attInfos = new Dictionary<string, TextInfo>();
  87.                 BlockTableRecord btr = (BlockTableRecord)br.BlockTableRecord.GetObject(OpenMode.ForRead);
  88.                 foreach (ObjectId id in btr)
  89.                 {
  90.                     if (id.ObjectClass.Name == "AcDbAttributeDefinition")
  91.                     {
  92.                         AttributeDefinition attDef = (AttributeDefinition)id.GetObject(OpenMode.ForRead);
  93.                         TextInfo ti = new TextInfo()
  94.                         {
  95.                             Position = attDef.Position,
  96.                             Alignment = attDef.AlignmentPoint,
  97.                             IsAligned = attDef.Justify != AttachmentPoint.BaseLeft,
  98.                             Rotation = attDef.Rotation
  99.                         };
  100.                         _attInfos.Add(attDef.Tag.ToUpper(), ti);
  101.                     }
  102.                 }
  103.             }
  104.  
  105.             protected override bool Update()
  106.             {
  107.                 base.Update();
  108.                 foreach (ObjectId id in _br.AttributeCollection)
  109.                 {
  110.                     AttributeReference att = (AttributeReference)id.GetObject(OpenMode.ForWrite);
  111.                     att.Rotation = _br.Rotation;
  112.                     string tag = att.Tag.ToUpper();
  113.                     if (_attInfos.ContainsKey(tag))
  114.                     {
  115.                         TextInfo ti = _attInfos[tag];
  116.                         att.Position = ti.Position.TransformBy(_br.BlockTransform);
  117.                         if (ti.IsAligned)
  118.                         {
  119.                             att.AlignmentPoint =
  120.                                 ti.Alignment.TransformBy(_br.BlockTransform);
  121.                             att.AdjustAlignment(_br.Database);
  122.                         }
  123.                         if (att.IsMTextAttribute)
  124.                         {
  125.                             att.UpdateMTextAttribute();
  126.                         }
  127.                         att.Rotation = ti.Rotation + _br.Rotation;
  128.                     }
  129.                 }
  130.                 return true;
  131.             }
  132.         }

A little using example to insert a block

Code - C#: [Select]
  1.         [CommandMethod("TEST")]
  2.         public void Test()
  3.         {
  4.             Document doc = Application.DocumentManager.MdiActiveDocument;
  5.             Database db = doc.Database;
  6.             Editor ed = doc.Editor;
  7.  
  8.             PromptResult pr = ed.GetString("\nBlock name: ");
  9.             if (pr.Status != PromptStatus.OK) return;
  10.             string blockName = pr.StringResult;
  11.  
  12.             using (Transaction tr = db.TransactionManager.StartTransaction())
  13.             {
  14.                 BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  15.                 if (!bt.Has(blockName))
  16.                 {
  17.                     ed.WriteMessage("\nNone block '{0}' in the document block table.", blockName);
  18.                     return;
  19.                 }
  20.                 BlockTableRecord curSpace =
  21.                     (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  22.  
  23.                 // Add the block reference to Database first
  24.                 BlockReference br = new BlockReference(Point3d.Origin, bt[blockName]);
  25.                 br.TransformBy(ed.CurrentUserCoordinateSystem);
  26.                 curSpace.AppendEntity(br);
  27.                 tr.AddNewlyCreatedDBObject(br, true);
  28.  
  29.                 // Get the block definition
  30.                 BlockTableRecord btr =
  31.                     (BlockTableRecord)tr.GetObject(bt[blockName], OpenMode.ForRead);
  32.                 BlockJig jig;
  33.                 if (btr.HasAttributeDefinitions)
  34.                 {
  35.                     // Add attribute references to the block reference
  36.                     foreach (ObjectId id in btr)
  37.                     {
  38.                         if (id.ObjectClass.Name == "AcDbAttributeDefinition")
  39.                         {
  40.                             AttributeDefinition attDef =
  41.                                 (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  42.                             AttributeReference attRef = new AttributeReference();
  43.                             attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  44.                             ObjectId attId = br.AttributeCollection.AppendAttribute(attRef);
  45.                             tr.AddNewlyCreatedDBObject(attRef, true);
  46.                         }
  47.                     }
  48.                     // Create a BlockAttribJig instance
  49.                     jig = new BlockAttribJig(br);
  50.                 }
  51.                 else
  52.                 {
  53.                     // Create a BlockJig instance
  54.                     jig = new BlockJig(br);
  55.                 }
  56.                 // Drag the block reference
  57.                 pr = ed.Drag(jig);
  58.                 if (pr.Status != PromptStatus.OK) br.Erase();
  59.                 tr.Commit();
  60.             }
  61.         }
Speaking English as a French Frog

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: 6150
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!?

Galway

  • Guest
Re: .NET BLOCK Routines
« Reply #30 on: November 24, 2014, 07:55:36 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

This is fantastic gile. One thing though, is there any way to keep the "Prompt" field of the attribute definitions? I've slightly modified this to act as an attribute definition copy tool (to let me select a source block and copy its attribute definitions to another block). The attribute definitions seemingly keep most of their properties when transferred over, but the "Prompt" fields show up blank. Poking around in ObjectArx and the debugger, I don't see any obvious way to do this, but maybe there's something I'm missing.

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #31 on: December 05, 2021, 12:36:31 PM »
Gile, I'm using the method extension which doesn't seem to work, the defining attributes don't sync. I have to use the manual ATTSYNC command for the update to work. Will it be something with the block, because it is dynamic?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #32 on: December 06, 2021, 01:21:04 AM »
Hi kelcyo,

It worked fpr me with the drawing you linked. You should give more details (AutoCAD version, context, ...).
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #33 on: December 06, 2021, 04:31:46 PM »
Hi gile, I just insert the block into the uploaded drawing in places determined by a list of points. I'm working with current version 2022.

Code: [Select]
// Get the current UCS Z axis (extrusion direction)
Matrix3d ucsMat = ed.CurrentUserCoordinateSystem;
CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d;
Vector3d zdir = ucsMat.CoordinateSystem3d.Zaxis;

// Get the OCS corresponding to UCS Z axis
Matrix3d ocsMat = MakeOcs(zdir);

// Transform the input point from UCS to OCS
//Point3d pt = ppr.Value.TransformBy(ucsMat.PreMultiplyBy(ocsMat));

// Get the X axis of the OCS
Vector3d ocsXdir = ocsMat.CoordinateSystem3d.Xaxis;

// Get the UCS rotation (angle between the OCS X axis and the UCS X axis)
double rot = ocsXdir.GetAngleTo(ucs.Xaxis, zdir);

BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

// Foreach in points collection
foreach (Point3d item in _pcIns)
{
    BlockReference br = new BlockReference(item, bt["MDF"]);
    br.Position = item;
    br.Rotation = rot;
    br.Normal = zdir;
    btr.AppendEntity(br);
    tr.AddNewlyCreatedDBObject(br, true);   

}

ObjectIdCollection brefIds = btr.GetBlockReferenceIds(false, true);
foreach (ObjectId id in brefIds)
{
    BlockReference bref = (BlockReference)tr.GetObject(id, OpenMode.ForWrite, false, true);
    bref.RecordGraphicsModified(true);
}

btr.SynchronizeAttributes();

tr.Commit();
« Last Edit: December 07, 2021, 10:11:59 AM by kelcyo »

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #34 on: December 07, 2021, 07:04:25 PM »
Anyone have any tips how to solve the problem?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #35 on: December 08, 2021, 01:38:30 AM »
Looking at your code, it looks like you're trying to synchronize the current Space (btr) attributes, not the "MDF" block definition.
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #36 on: December 08, 2021, 12:16:45 PM »
Even making your indicated change, there is an error in the timing, the attribute is not filled in correctly, it inserts the content of the standard block attribute and not the reference field...

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #37 on: December 08, 2021, 01:38:05 PM »
Here's a simple testing code:
Code - C#: [Select]
  1.     [CommandMethod("TEST")]
  2.     public static void Test()
  3.     {
  4.         var doc = AcAp.DocumentManager.MdiActiveDocument;
  5.         var db = doc.Database;
  6.         var ed = doc.Editor;
  7.         using (var tr = db.TransactionManager.StartTransaction())
  8.         {
  9.             var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  10.             if (bt.Has("MDF"))
  11.             {
  12.                 var btr = (BlockTableRecord)tr.GetObject(bt["MDF"], OpenMode.ForRead);
  13.                 btr.SynchronizeAttributes();
  14.             }
  15.             tr.Commit();
  16.         }
  17.         ed.Regen();
  18.     }

Here's a screencast which shows this code in action with your block: https://autode.sk/3EExoV1
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #38 on: December 08, 2021, 06:44:57 PM »
It's not working correctly with me, the attributes remain with the wrong content...



Code: [Select]
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
if (!bt.Has("MDF"))
{
    ed.WriteMessage("\nBloco modelo não localizado.");
    return;
}


// Get the current UCS Z axis (extrusion direction)
Matrix3d ucsMat = ed.CurrentUserCoordinateSystem;
CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d;
Vector3d zdir = ucsMat.CoordinateSystem3d.Zaxis;

// Get the OCS corresponding to UCS Z axis
Matrix3d ocsMat = MakeOcs(zdir);

// Transform the input point from UCS to OCS
//Point3d pt = ppr.Value.TransformBy(ucsMat.PreMultiplyBy(ocsMat));

// Get the X axis of the OCS
Vector3d ocsXdir = ocsMat.CoordinateSystem3d.Xaxis;

// Get the UCS rotation (angle between the OCS X axis and the UCS X axis)
double rot = ocsXdir.GetAngleTo(ucs.Xaxis, zdir);

BlockTableRecord curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

// Foreach in points collection
foreach (PointsLocation item in _pcIns)
{
   
    BlockReference br = new BlockReference(item._ponto, bt["MDF"]);
    br.Position = item._ponto;
    br.Rotation = rot;   
    br.Normal = zdir;

    ObjectId _newid = curSpace.AppendEntity(br);
    tr.AddNewlyCreatedDBObject(br, true);


    if (br.IsDynamicBlock && ids.Length == 2)
    {
        DynamicBlockReferencePropertyCollection props = br.DynamicBlockReferencePropertyCollection;

        foreach (DynamicBlockReferenceProperty prop in props)
        {
            object[] values = prop.GetAllowedValues();


            if (item._ordem == 0)
            {
                //Switch Property
                switch (prop.PropertyName)
                {
case "Distancia_Texto":
    prop.Value = 18.0;
    break;
case "Anglo_Texto":
    prop.Value = 45.0;
    break;
case "Anglo_Marco":
    prop.Value = 0.0;
    break;
                }

            }
            else
            {
                switch (prop.PropertyName)
                {
case "Distancia_Texto":
    prop.Value = 18.0;
    break;
case "Anglo_Texto":
    prop.Value = 225.0;
    break;
case "Anglo_Marco":
    prop.Value = 0.0;
    break;
                }
            }

        }
    }   

}

var btr = (BlockTableRecord)tr.GetObject(bt["MDF"], OpenMode.ForRead);
btr.SynchronizeAttributes();

tr.Commit();

https://autode.sk/3GuUNJ0

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #39 on: December 09, 2021, 01:50:07 AM »
Your problem is not due to the SynchronizeAttribute method (which does not seem to be rquired as you do not change attributes properties).
Your code simply does not add attribute references to the block reference. While insertin an attributed block reference, you have to explicitly create the attribute references. See this topic.
« Last Edit: December 09, 2021, 04:18:21 AM by gile »
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #40 on: December 09, 2021, 02:14:29 PM »
Gile, I was really wrong... I needed to add the attribute references, but the detail was found in the inclusion of the field within the attribute. This was perfect, thanks for the tip...

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #41 on: December 09, 2021, 02:34:27 PM »
Here's an example:
Code - C#: [Select]
  1. [CommandMethod("TEST")]
  2. public static void Test()
  3. {
  4.     var doc = AcAp.DocumentManager.MdiActiveDocument;
  5.     var db = doc.Database;
  6.     var ed = doc.Editor;
  7.     using (var tr = db.TransactionManager.StartTransaction())
  8.     {
  9.         var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  10.         if (bt.Has("MDF"))
  11.         {
  12.             var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  13.             var br = new BlockReference(new Point3d(30.0, 15.0, 0.0), bt["MDF"]);
  14.             curSpace.AppendEntity(br);
  15.             tr.AddNewlyCreatedDBObject(br, true);
  16.  
  17.             // add attribute references to the block reference
  18.             var btr = (BlockTableRecord)tr.GetObject(bt["MDF"], OpenMode.ForRead);
  19.             foreach (ObjectId id in btr)
  20.             {
  21.                 if (id.ObjectClass.DxfName == "ATTDEF")
  22.                 {
  23.                     var attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  24.                     if (!attDef.Constant)
  25.                     {
  26.                         var attRef = new AttributeReference();
  27.                         attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  28.                         br.AttributeCollection.AppendAttribute(attRef);
  29.                         tr.AddNewlyCreatedDBObject(attRef, true);
  30.                         if (attRef.HasFields)
  31.                         {
  32.                             attRef.TextString = attRef.getTextWithFieldCodes().Replace(
  33.                                 "?BlockRefId",
  34.                                 $@"%<\_ObjId {br.ObjectId.ToString().Trim('(', ')')}>%");
  35.                         }
  36.  
  37.                     }
  38.                 }
  39.             }
  40.  
  41.             // set dynamic properties
  42.             foreach (DynamicBlockReferenceProperty prop in br.DynamicBlockReferencePropertyCollection)
  43.             {
  44.                 switch (prop.PropertyName)
  45.                 {
  46.                     case "Distancia_Texto":
  47.                         prop.Value = 18.0;
  48.                         break;
  49.                     case "Anglo_Texto":
  50.                         prop.Value = 45.0 * Math.PI / 180.0;
  51.                         break;
  52.                     default:
  53.                         break;
  54.                 }
  55.             }
  56.         }
  57.         tr.Commit();
  58.     }
  59.     ed.Regen();
  60. }
Speaking English as a French Frog