• Industries
  • Products
  • Buy
  • Services & Support
  • Communities
  • Discussion Groups

    .NET

    Reply
    Distinguished Contributor
    vince1327
    Posts: 117
    Registered: ‎11-02-2011
    Accepted Solution

    Repeatedly Insert Block

    889 Views, 35 Replies
    01-17-2012 06:06 AM

    Hey everyone,

     

    So i've been able to successfully import a single block at a time into an active drawing, now i'm trying to insert the same block multiple times to essentially create a layout within the drawing. How would i go about inserting a selected block "x" amount of times. For a better idea of where i'm stuck, i've commented a bit of code below with a better explanation.  What i've got so far is this:

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using Autodesk.AutoCAD.Runtime;
    using Autodesk.AutoCAD.ApplicationServices;
    using Autodesk.AutoCAD.DatabaseServices;
    using Autodesk.AutoCAD.Geometry;
    using Autodesk.AutoCAD.EditorInput;
    using Autodesk.AutoCAD.DataExtraction;
    using Autodesk.AutoCAD.Colors;
    using DB_Extraction;
    using acadApp = Autodesk.AutoCAD.ApplicationServices.Application;

    namespace CHTEST
    {
    public class CHTEST1
    {
    // This is the name of my command

    [Autodesk.AutoCAD.Runtime.CommandMethod("CHTEST")]

    public void CHTEST1()
    {
    {

    Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    Point3d pMax = new Point3d();
    string comp = "C:\\ACADSTUFF\\test.dwg";
    // int numsprink = 5;

    Transaction tr = doc.TransactionManager.StartTransaction();
    using (DocumentLock acLckDoc = doc.LockDocument())

    try
    {

    //Check to see if the layer CH-TST already exists, if so use it, if not create it and then use it
    string sLayerName1 = "CH-TST";

    LayerTable acLyrTbl1; acLyrTbl1 = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
    LayerTableRecord acLyrTblRec;

    if (acLyrTbl1.Has(sLayerName1) == false)
    {
    acLyrTblRec = new LayerTableRecord();
    // Assign the layer a name
    acLyrTblRec.Name = sLayerName1;

    // Upgrade the Layer table for write
    if (acLyrTbl1.IsWriteEnabled == false) acLyrTbl1.UpgradeOpen();

    // Append the new layer to the Layer table and the transaction
    acLyrTbl1.Add(acLyrTblRec);
    tr.AddNewlyCreatedDBObject(acLyrTblRec, true);
    }
    else
    {
    // Open the layer if it already exists for write
    acLyrTblRec = tr.GetObject(acLyrTbl1[sLayerName1], OpenMode.ForWrite) as LayerTableRecord;

    }


    //This partwill import the selected dwg into modelspace and activate it
    string dwgName = HostApplicationServices.Current.FindFile(comp, acadApp.DocumentManager.MdiActiveDocument.Database, FindFileHint.Default);

    Database db1 = new Database(false, false);

    db1.ReadDwgFile(dwgName, System.IO.FileShare.Read, true, "");

    ObjectId BlkId;

    BlkId = doc.Database.Insert(dwgName, db1, false);

    BlockTable bt = (BlockTable)tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead, true);

    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);


    //ideally this next section will iterate the import process "x" amount of times and allow for effetive block placement!

    // Below is part of some code that i had used to copy a paperspace layout "x" amount of times. I understand the iteration part but am not sure how to apply it to a block. I don't know how to reference the block that i intend to insert and then have it populate "x" amount of times.
    /*
    for (int i = 1; i <= numLayouts; i++)
    {
    lm.CopyLayout("Layout1", "SP-" + i.ToString());
    Layout layout = (Layout)trx.GetObject(lm.GetLayoutId("SP-" + i.ToString()), OpenMode.ForWrite);

    }

    */


    //FACSPR7

     

     

    BlockReference bref = new BlockReference(pMax, BlkId);

    bref.Layer = "CH-TST";

    btr.AppendEntity(bref);

    tr.AddNewlyCreatedDBObject(bref, true);

    bref.ExplodeToOwnerSpace();

    bref.Erase();

    tr.Commit();


    }
    catch
    {
    MessageBox.Show("Failed to read the DWG from the refence folder, are you sure it is present?");
    }
    finally
    {
    tr.Dispose();
    }

     


    }

    }
    }

    }

     

     

     

    Please use plain text.
    Distinguished Contributor
    VB_Autocad_guy
    Posts: 136
    Registered: ‎07-24-2009

    Re: Repeatedly Insert Block

    01-17-2012 08:11 AM in reply to: vince1327

    Can you clarify a bit? I'll give it a try. But maybe I'm missing your point. 

     

    You could just use the same command basically block insert several times and them DB commit when you're done. 

    Or just create several instances of a blockreference. Or you can use the copy object command. 

     

    Instead of Insert MText entity like in the code below insert a BlockReference Entity

    ( Dim myBRef As BlockReference)

     

    I got this code from:

    AutoCAD® .NET Basics, Part I
    Stephen Preston – Autodesk Inc

     

    Hope you don't mind I'm using VB.NET

     

    Imports Autodesk.AutoCAD.Runtime
    Imports Autodesk.AutoCAD.ApplicationServices
    Imports Autodesk.AutoCAD.DatabaseServices
    
        Public Class Class1
            <CommandMethod("HelloWorld")> _
            Public Sub MyMethod()
                ' Some variable definitions
                Dim DB As Database
                Dim BT As BlockTable
                Dim MS As BlockTableRecord
                ' Start a transaction to edit the DWG database
                Using trans As Transaction = Application.DocumentManager.MdiActiveDocument.TransactionManager.StartT()
                    ransaction()
                    ' Open ModelSpace BlockTableRecord ready to add entity
                    DB = Application.DocumentManager.MdiActiveDocument.Database
                    BT = trans.GetObject(DB.BlockTableId, OpenMode.ForRead)
                    MS = trans.GetObject(BT(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
    
                    ' Create a new MText entity and set its text
                    Dim txt As MText = New MText
                    txt.Contents = "Hello World from VB.NET"
    ' Add the MText to ModelSpace MS.AppendEntity(txt)
    ' Add the Mtext to the transaction trans.AddNewlyCreatedDBObject(txt, True)
    ' Commit transaction so changes persist in the DWG trans.Commit() End Using End Sub End Class

     





    Please use plain text.
    Distinguished Contributor
    vince1327
    Posts: 117
    Registered: ‎11-02-2011

    Re: Repeatedly Insert Block

    01-17-2012 08:35 AM in reply to: VB_Autocad_guy

    For clarification, i can already insert a block, i essentially need to have an argument for my command in which i can specify a positive integer. So if i select the block i want to insert, it will ask me how for how many i would like to insert. I've got pretty much everything downpact except how to insert a block "x" amount of times over. I was thinking of two possible ways. 1.) This would involve just running the command i've posted "x" amount of times, but i'm unsure of the syntax or where to begin. 2.) Copy the block i've chosen to insert "x" amount of times -1. However, i'm still unsure of the syntax or where to begin. From a logic point of view i understand what i have to do, i've got a good amount of experience programming in a variety of languages however this AutoCAD API never ceases to confuse me. What you've posted below makes sense, but im looking more for a way to achieve what you've posted "x" amount of times.

     

    Thanks!

    Please use plain text.
    Active Contributor
    cincir
    Posts: 32
    Registered: ‎08-12-2011

    Re: Repeatedly Insert Block

    01-19-2012 12:29 PM in reply to: vince1327

    vince1327 can you tell me ; do you want to insert a block definition x number of times or do you want to create a blockreference of a block definition you have x number of times. maybe i got you wrong but just to be clear :

    - do we aggree that a block definition is a blocktablerecord in blocktable symbol table that has entities in it?

    - do we aggree that a blockreference is a like an entity that references a blocktablerecord.

     

    i mean the block definition is the blueprint while the blockreference is the actual objects in the drawing.

     

    as i said before maybe i got you wrong (if so please discard this post) but you want to insert objects to the drawing. so instead of inserting blocktablerecord s xnumber of times try to insert blockreferences of that blocktablerecord x number of times.

     

    if that is what you mean please confirm for code example.

    Please use plain text.
    Distinguished Contributor
    vince1327
    Posts: 117
    Registered: ‎11-02-2011

    Re: Repeatedly Insert Block

    01-19-2012 12:58 PM in reply to: cincir

    Hi Cincir,

     

    I'm new to AutoCAD overall and i've just spoken with one of our designers who has indeed confirmed what you've said. I'm looking to insert blockreferences of my block "x" number of times. The AutoCAD API is tricky to learn and is sometimes a bit confusing so i appreciate your time trying to help me find what i'm looking for. If you have an example of how to do this, it would be amazing!

     

    Thanks

     

    Please use plain text.
    Active Contributor
    cincir
    Posts: 32
    Registered: ‎08-12-2011

    Re: Repeatedly Insert Block

    01-19-2012 01:41 PM in reply to: vince1327

    [CommandMethod("CreateBlockReference")]

    public void CreateBlockReference()

    {

    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    Editor acEd = acDoc.Editor;

     

    PromptStringOptions acPSO = new PromptStringOptions("Enter the name of block you want to insert:");

    PromptResult acPR = acEd.GetString(acPSO);

     

    if (acPR.Status == PromptStatus.OK)

    {

    using (Transaction acTrans = acDoc.TransactionManager.StartTransaction())

    {

    BlockTable acBlkTbl = acTrans.GetObject(acDoc.Database.BlockTableId, OpenMode.ForRead) as BlockTable;

    if (acBlkTbl.Has(acPR.StringResult) == true) // Our BlockTable has block definition that we enter

    {

    ObjectId acBlockId = acTrans.GetObject(acBlkTbl[acPR.StringResult],OpenMode.ForRead);

     

    PromptIntegerOptions acPIO = new PromptInterOptions("Enter the number of times block will be inserted:");

    acPIO.AllowNegative = false;

    acPIO.AllowZero = false;

    PromptIntegerResult acPIR = acEd.GetInteger(acPIO);

     

    if (acPIR.Status == PromptStatus.OK)

    {

    BlockTableRecord acBlkTblRec = acTrans.GetObject(BlockTableRecord.ModelSpace,OpenMode.ForWrite) as BlockTableRecord;

     

    Point3d pCenter = new Point3d(0,0,0);

    Vector3d vOffset = new Vector3d(10,0,0);

     

    for (int i = 0;i < acPIR.Value ; i++)

    {

    BlockReference acBR = new BlockReference(pCenter , acBlockId);

    acBlkTblRec.AppendEntity(acBR);

    acTrans.AddNewlyCreatedObject(acBR,true);

    pCenter += vOffset;

    }

    }

    }

     

    acTrans.Commit();

    }

    }

    }

     

    i just wrote it i mean didnt copy-paste so may have syntax errors. feel free to ask questions for this or other stuff.

     

    good luck.

    Please use plain text.
    Distinguished Contributor
    VB_Autocad_guy
    Posts: 136
    Registered: ‎07-24-2009

    Re: Repeatedly Insert Block

    01-20-2012 07:28 AM in reply to: cincir

    Like Cincir said: 

     

    For each Block Reference use 

    .AppendEntity 

    .AddNewlyCreatedObject

     

    Those are your guys. 

     

    It's sure good to hear I'm not the only one who thinks the Autocad API is confusing. 

     

    Please use plain text.
    Distinguished Contributor
    vince1327
    Posts: 117
    Registered: ‎11-02-2011

    Re: Repeatedly Insert Block

    01-23-2012 12:05 PM in reply to: vince1327

    Thanks for the help so far! I'm just stuck on one line below. I get this error in VS 2010 "

    Error 1 Cannot implicitly convert type 'Autodesk.AutoCAD.DatabaseServices.BlockTable' to 'Autodesk.AutoCAD.DatabaseServices.ObjectId'" How do i re-arrange this to work?

     

    Thanks

     

     

    namespace TEST
    {
    public class TEST
    {
    // This is the name of my command

    [Autodesk.AutoCAD.Runtime.CommandMethod("CHTEST")]

    public void TEST()
    {
    Document acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    Editor acEd = acDoc.Editor;
    string comp = "C:\\ACADSTUFF\\test.dwg";

    PromptStringOptions acPSO = new PromptStringOptions("Enter the name of block you want to insert:");
    PromptResult acPR = acEd.GetString(acPSO);

    if (acPR.Status == PromptStatus.OK)
    {
    using (Transaction acTrans = acDoc.TransactionManager.StartTransaction())
    {
    BlockTable acBlkTbl = acTrans.GetObject(acDoc.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
    if (acBlkTbl.Has(acPR.StringResult) == true) // Our BlockTable has block definition that we enter
    {

    ObjectId acBlockId = (BlockTable)acTrans.GetObject(acBlkTbl[acPR.StringResult], OpenMode.ForRead);


    //ObjectId acBlockId = (BlockTableRecord)acTrans.GetObject(btr[acPR.StringResult], OpenMode.ForWrite);
    //ObjectId acBlockId = acTrans.GetObject(acBlkTbl[acPR.StringResult],OpenMode.ForRead);

    PromptIntegerOptions acPIO = new PromptIntegerOptions("Enter the number of times block will be inserted:");
    acPIO.AllowNegative = false;
    acPIO.AllowZero = false;
    PromptIntegerResult acPIR = acEd.GetInteger(acPIO);

    if (acPIR.Status == PromptStatus.OK)
    {

    BlockTable bt = (BlockTable)acTrans.GetObject(acDoc.Database.BlockTableId, OpenMode.ForRead, true);
    BlockTableRecord acBlkTblRec = (BlockTableRecord)acTrans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

    Point3d pCenter = new Point3d(0, 0, 0);
    Vector3d vOffset = new Vector3d(10, 0, 0);

    for (int i = 0; i < acPIR.Value; i++)
    {
    BlockReference acBR = new BlockReference(pCenter, acBlockId);
    acBlkTblRec.AppendEntity(acBR);
    acTrans.AddNewlyCreatedDBObject(acBR, true);
    pCenter += vOffset;
    }
    }
    }

    acTrans.Commit();
    }
    }
    }
    }

    }

    Please use plain text.
    Distinguished Contributor
    VB_Autocad_guy
    Posts: 136
    Registered: ‎07-24-2009

    Re: Repeatedly Insert Block

    01-23-2012 01:17 PM in reply to: vince1327

    Hmm.... 

    ObjectId acBlockId = (BlockTable)acTrans.GetObject(acBlkTbl[acPR.StringResult], OpenMode.ForRead);

     

    Why don't you skip that last line of code? 

    Just use: BlockReference acBR = new BlockReference(pCenter, myBlockTable(BlockName));

     

    That way you don't have to worry about the object conflict your coming across. 

    And as long as a block with that name exists your fine. 

     

    Okay so maybe this doesn't help at all. But I figure at least try to excercise my programming brain muscle to make it stronger :smileyhappy:

     

    Good Luck!

     

    Maybe this will help:

    Public Function ModelSpaceInsertBlock(ByVal InsPt As Geometry.Point3d, _
    	ByVal BlockName As String, ByVal XScale As Double, _
    	ByVal YScale As Double, ByVal ZScale As Double) As DatabaseServices.ObjectId
    		Dim myBlkID As ObjectId = ObjectId.Null
    		Try
    
    			Dim myDwg As Document = Application.DocumentManager.MdiActiveDocument
    			Using myTrans As Transaction = myDwg.TransactionManager.StartTransaction
    
    				'Open the database for Write
    				Dim myBT As BlockTable = myDwg.Database.BlockTableId.GetObject(OpenMode.ForRead)
    				Dim myModelSpace As BlockTableRecord = myBT(BlockTableRecord.ModelSpace).GetObject(OpenMode.ForWrite)
    
    				'Insert the Block
    				Dim myBlockDef As BlockTableRecord = myBT(BlockName).GetObject(OpenMode.ForRead)
    				Dim myBlockRef As New DatabaseServices.BlockReference(InsPt, myBT(BlockName))
    				myBlockRef.ScaleFactors = New Geometry.Scale3d(XScale, YScale, ZScale)
    
    				myModelSpace.AppendEntity(myBlockRef)
    				myTrans.AddNewlyCreatedDBObject(myBlockRef, True)
    				myBlkID = myBlockRef.ObjectId
    
    				'Append Attribute References to the BlockReference
    				Dim myAttColl As DatabaseServices.AttributeCollection
    				Dim myEntID As ObjectId
    				Dim myEnt As DatabaseServices.Entity
    				myAttColl = myBlockRef.AttributeCollection
    				For Each myEntID In myBlockDef
    					myEnt = myEntID.GetObject(OpenMode.ForWrite)
    					If TypeOf myEnt Is DatabaseServices.AttributeDefinition Then
    						Dim myAttDef As DatabaseServices.AttributeDefinition = CType(myEnt, AttributeDefinition)
    						Dim myAttRef As New DatabaseServices.AttributeReference
    						myAttRef.SetAttributeFromBlock(myAttDef, myBlockRef.BlockTransform)
    						myAttColl.AppendAttribute(myAttRef)
    						myTrans.AddNewlyCreatedDBObject(myAttRef, True)
    					End If
    				Next
    
    				'Commit the Transaction
    				myTrans.Commit()
    			End Using
    		Catch ex As Exception
    			myBlkID = ObjectId.Null
    		End Try
    
    		Return myBlkID
    	End Function

     

     

    I also found this snippet in C#

     

    using Autodesk.AutoCAD.ApplicationServices;
    using Autodesk.AutoCAD.DatabaseServices;
    using Autodesk.AutoCAD.EditorInput;
    using Autodesk.AutoCAD.Geometry;
    using Autodesk.AutoCAD.Runtime;
    
    namespace BlockInsertion
    {
        public class Class1
        {
            [CommandMethod("Test")]
            public void Test()
            {
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Database db = doc.Database;
                Editor ed = doc.Editor;
    
                // Get the block name
                PromptStringOptions pso = new PromptStringOptions("\nEnter block name: ");
                PromptResult pr = ed.GetString(pso);
                if (pr.Status == PromptStatus.OK)
                {
                    // Start a transaction
                    using (Transaction tr = db.TransactionManager.StartTransaction())
                    {
                        // Test if block exists in the block table
                        BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                        if (!bt.Has(pr.StringResult))
                        {
                            ed.WriteMessage("\nBlock not found.");
                        }
                        else
                        {
                            ObjectId id = bt[pr.StringResult];
    
                            // Get insertion point
                            PromptPointOptions ppo = new PromptPointOptions("\nSpecify insertion point: ");
                            PromptPointResult ppr = ed.GetPoint(ppo);
                            if (ppr.Status == PromptStatus.OK)
                            {
                                Point3d pt = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
    
                                // Create a block reference
                                BlockReference br = new BlockReference(pt, id);
    
                                // Get Model space
                                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
    
                                // Add the block reference to Model space
                                btr.AppendEntity(br);
                                tr.AddNewlyCreatedDBObject(br, true);
                            }
                        }
    
                        // Commit the transaction
                        tr.Commit();
                    }
                }
            }
        }
    }

     

    Please use plain text.
    Active Contributor
    cincir
    Posts: 32
    Registered: ‎08-12-2011

    Re: Repeatedly Insert Block

    01-23-2012 03:35 PM in reply to: VB_Autocad_guy

    ObjectId acBlockId = acTrans.GetObject(acBlkTbl[acPR.StringResult],OpenMode.ForRead);

     

    change this line to

     

    ObjectId acBlockId = acBlkTbl[acPR.StringResult];

     

    as i said before i just typed it not copy - paste. 

    Please use plain text.