Using a jig from .NET to multiply insert AutoCAD blocks - Part 2
In the last post we looked at a jig that can be used to add block references to an AutoCAD drawing. This post extends that code to support annotative block definitions (available from AutoCAD 2008) and blocks with attributes. Thanks once again to Holger Steiner for the jig class and to Roland Feletic for posting the code to support annotative blocks.
A comment on the previous post asked about having attributes visible during the jig process: unfortunately that's not currently possible, as the existing managed AttributeCollection implementation wraps the version of the AcDbBlockReference::appendAttribute() ObjectARX function that requires the block reference to already have been added to the drawing. So for now you will need to live with the fact that the attributes don't display during the jig, but do display as soon as the block has been added to the drawing.
Below is the modified C# code, with line numbers. And here's the source file for download.
1 using Autodesk.AutoCAD.ApplicationServices;
2 using Autodesk.AutoCAD.DatabaseServices;
3 using Autodesk.AutoCAD.EditorInput;
4 using Autodesk.AutoCAD.Runtime;
5 using Autodesk.AutoCAD.Geometry;
6 using Autodesk.AutoCAD.Internal;
7
8 namespace BlockJigTest
9 {
10 class BlockJig : EntityJig
11 {
12 Point3d mCenterPt, mActualPoint;
13
14 public BlockJig(BlockReference br)
15 : base(br)
16 {
17 mCenterPt = br.Position;
18 }
19
20 protected override SamplerStatus Sampler(JigPrompts prompts)
21 {
22 JigPromptPointOptions jigOpts =
23 new JigPromptPointOptions();
24 jigOpts.UserInputControls =
25 (UserInputControls.Accept3dCoordinates
26 | UserInputControls.NoZeroResponseAccepted
27 | UserInputControls.NoNegativeResponseAccepted);
28
29 jigOpts.Message =
30 "\nEnter insert point: ";
31
32 PromptPointResult dres =
33 prompts.AcquirePoint(jigOpts);
34
35 if (mActualPoint == dres.Value)
36 {
37 return SamplerStatus.NoChange;
38 }
39 else
40 {
41 mActualPoint = dres.Value;
42 }
43 return SamplerStatus.OK;
44 }
45
46 protected override bool Update()
47 {
48 mCenterPt = mActualPoint;
49 try
50 {
51 ((BlockReference)Entity).Position = mCenterPt;
52 }
53 catch (System.Exception)
54 {
55 return false;
56 }
57 return true;
58 }
59
60 public Entity GetEntity()
61 {
62 return Entity;
63 }
64 }
65
66 public class Commands
67 {
68 [CommandMethod("BJIG")]
69 public void CreateBlockWithJig()
70 {
71 Document doc =
72 Application.DocumentManager.MdiActiveDocument;
73 Database db = doc.Database;
74 Editor ed = doc.Editor;
75
76 // First let's get the name of the block
77 PromptStringOptions opts =
78 new PromptStringOptions("\nEnter block name: ");
79 PromptResult pr = ed.GetString(opts);
80 if (pr.Status == PromptStatus.OK)
81 {
82 Transaction tr =
83 doc.TransactionManager.StartTransaction();
84 using (tr)
85 {
86 // Then open the block table and check the
87 // block definition exists
88 BlockTable bt =
89 (BlockTable)tr.GetObject(
90 db.BlockTableId,
91 OpenMode.ForRead
92 );
93 if (!bt.Has(pr.StringResult))
94 {
95 ed.WriteMessage("\nBlock not found.");
96 }
97 else
98 {
99 ObjectId bdId = bt[pr.StringResult];
100
101 // We loop until the jig is cancelled
102 while (pr.Status == PromptStatus.OK)
103 {
104 // Create the block reference and
105 // add it to the jig
106 Point3d pt = new Point3d(0, 0, 0);
107 BlockReference br =
108 new BlockReference(pt, bdId);
109
110 BlockJig entJig = new BlockJig(br);
111
112 // Perform the jig operation
113 pr = ed.Drag(entJig);
114 if (pr.Status == PromptStatus.OK)
115 {
116 // If all is OK, let's go and add the
117 // entity to the modelspace
118 BlockTableRecord ms =
119 (BlockTableRecord)tr.GetObject(
120 bt[BlockTableRecord.ModelSpace],
121 OpenMode.ForWrite
122 );
123 ms.AppendEntity(
124 entJig.GetEntity()
125 );
126 tr.AddNewlyCreatedDBObject(
127 entJig.GetEntity(),
128 true
129 );
130
131 // Start attrib/annot-scale support code
132 BlockTableRecord bd =
133 (BlockTableRecord)tr.GetObject(
134 bdId,
135 OpenMode.ForRead
136 );
137 if (bd.Annotative == AnnotativeStates.True)
138 {
139 ObjectContextManager ocm =
140 db.ObjectContextManager;
141 ObjectContextCollection occ =
142 ocm.GetContextCollection(
143 "ACDB_ANNOTATIONSCALES"
144 );
145 ObjectContexts.AddContext(
146 br,
147 occ.CurrentContext
148 );
149 }
150
151 // Add the attributes
152 foreach (ObjectId attId in bd)
153 {
154 Entity ent =
155 (Entity)tr.GetObject(
156 attId,
157 OpenMode.ForRead
158 );
159 if (ent is AttributeDefinition)
160 {
161 AttributeDefinition ad =
162 (AttributeDefinition)ent;
163 AttributeReference ar =
164 new AttributeReference();
165 ar.SetAttributeFromBlock(
166 ad,
167 br.BlockTransform
168 );
169 br.AttributeCollection.AppendAttribute(ar);
170 tr.AddNewlyCreatedDBObject(ar, true);
171 }
172 }
173 // End attrib/annot-scale support code
174
175 // Call a function to make the graphics display
176 // (otherwise it will only do so when we Commit)
177 doc.TransactionManager.QueueForGraphicsFlush();
178 }
179 }
180 }
181 tr.Commit();
182 }
183 }
184 }
185 }
186 }
You'll notice the lines in red, from lines 131 to 173, have been added to the previous version of the code. The lines from 137 to 149 will only work with versions since AutoCAD 2008, as they're related to annotation scaling. As mentioned previously, this section of code relies on AcMgdInternal.dll, an unsupported assembly which is liable to change in future releases.
Update:
Subsequent to the comment from Roland, I've modified the above code to attach the annotation scale earlier, before the jig starts. This allows the jig to properly represent the block being placed (as otherwise annotative blocks will not display during the jig operation):
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Internal;
namespace BlockJigTest
{
class BlockJig : EntityJig
{
Point3d mCenterPt, mActualPoint;
public BlockJig(BlockReference br)
: base(br)
{
mCenterPt = br.Position;
}
protected override SamplerStatus Sampler(JigPrompts prompts)
{
JigPromptPointOptions jigOpts =
new JigPromptPointOptions();
jigOpts.UserInputControls =
(UserInputControls.Accept3dCoordinates
| UserInputControls.NoZeroResponseAccepted
| UserInputControls.NoNegativeResponseAccepted);
jigOpts.Message =
"\nEnter insert point: ";
PromptPointResult dres =
prompts.AcquirePoint(jigOpts);
if (mActualPoint == dres.Value)
{
return SamplerStatus.NoChange;
}
else
{
mActualPoint = dres.Value;
}
return SamplerStatus.OK;
}
protected override bool Update()
{
mCenterPt = mActualPoint;
try
{
((BlockReference)Entity).Position = mCenterPt;
}
catch (System.Exception)
{
return false;
}
return true;
}
public Entity GetEntity()
{
return Entity;
}
}
public class Commands
{
[CommandMethod("BJIG")]
public void CreateBlockWithJig()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// First let's get the name of the block
PromptStringOptions opts =
new PromptStringOptions("\nEnter block name: ");
PromptResult pr = ed.GetString(opts);
if (pr.Status == PromptStatus.OK)
{
Transaction tr =
doc.TransactionManager.StartTransaction();
using (tr)
{
// Then open the block table and check the
// block definition exists
BlockTable bt =
(BlockTable)tr.GetObject(
db.BlockTableId,
OpenMode.ForRead
);
if (!bt.Has(pr.StringResult))
{
ed.WriteMessage("\nBlock not found.");
}
else
{
ObjectId bdId = bt[pr.StringResult];
// We loop until the jig is cancelled
while (pr.Status == PromptStatus.OK)
{
// Create the block reference and
// add it to the jig
Point3d pt = new Point3d(0, 0, 0);
BlockReference br =
new BlockReference(pt, bdId);
// Start annot-scale support code
BlockTableRecord bd =
(BlockTableRecord)tr.GetObject(
bdId,
OpenMode.ForRead
);
// Using will dispose of the block definition
// when no longer needed
using (bd)
{
if (bd.Annotative == AnnotativeStates.True)
{
ObjectContextManager ocm =
db.ObjectContextManager;
ObjectContextCollection occ =
ocm.GetContextCollection(
"ACDB_ANNOTATIONSCALES"
);
ObjectContexts.AddContext(
br,
occ.CurrentContext
);
}
}
// End annot-scale support code
BlockJig entJig = new BlockJig(br);
// Perform the jig operation
pr = ed.Drag(entJig);
if (pr.Status == PromptStatus.OK)
{
// If all is OK, let's go and add the
// entity to the modelspace
BlockTableRecord ms =
(BlockTableRecord)tr.GetObject(
bt[BlockTableRecord.ModelSpace],
OpenMode.ForWrite
);
ms.AppendEntity(
entJig.GetEntity()
);
tr.AddNewlyCreatedDBObject(
entJig.GetEntity(),
true
);
// Start attribute support code
bd =
(BlockTableRecord)tr.GetObject(
bdId,
OpenMode.ForRead
);
// Add the attributes
foreach (ObjectId attId in bd)
{
Entity ent =
(Entity)tr.GetObject(
attId,
OpenMode.ForRead
);
if (ent is AttributeDefinition)
{
AttributeDefinition ad =
(AttributeDefinition)ent;
AttributeReference ar =
new AttributeReference();
ar.SetAttributeFromBlock(
ad,
br.BlockTransform
);
br.AttributeCollection.AppendAttribute(ar);
tr.AddNewlyCreatedDBObject(ar, true);
}
}
// End attribute support code
// Call a function to make the graphics display
// (otherwise it will only do so when we Commit)
doc.TransactionManager.QueueForGraphicsFlush();
}
}
}
tr.Commit();
}
}
}
}
}
Update 2:
This more recent post shows how to jig blocks with attributes.

Subscribe via RSS
Thank you Kean for this code.
I was searching so long for for this one: "doc.TransactionManager.QueueForGraphicsFlush();"
Posted by: Roland Feletic | May 22, 2007 at 08:32 AM
Hi Kean,
I had some code like yours and changed it now that it works with annotative blocks. I just want to say that it is not possible to use the jig-example with annotative-blocks. It is not possible to jig annotative-blocks, well, it is possible but you will not see them. Is there any chance to add the annotation-scale during the jig?
Roland
Posted by: Roland Feletic | May 22, 2007 at 09:59 AM
Thanks, Roland. Hopefully the above code now fixes it...
Kean
Posted by: Kean | May 22, 2007 at 10:31 AM
Thank you, Kean, this new code works really perfect.
Roland
Posted by: Roland Feletic | May 22, 2007 at 12:25 PM
Hi Kean,
just one thing I miss in AutoCAD (eg. measure) and in this program is the support of the units of the block. Therefore I would change the code a little bit and insert two more lines.
using (bd)
{
if (bd.Annotative == AnnotativeStates.True)
{
ObjectContextManager ocm = db.ObjectContextManager;
ObjectContextCollection occ = ocm.GetContextCollection("ACDB_ANNOTATIONSCALES");
ObjectContexts.AddContext(br, occ.CurrentContext);
}
else
br.ScaleFactors = new Scale3d(br.UnitFactor);
}
Roland
Posted by: Roland Feletic | May 23, 2007 at 05:09 PM
Thanks, Roland!
Kean
Posted by: Kean | May 24, 2007 at 03:45 AM
Hi Kean
Is it possible to append attributes to the blockref before drag-operation? I get an "eNoDatabase" error when i try. I know the object isn't added to the DB until after drag.
Cheers
Tore
Posted by: Tore | May 25, 2007 at 01:37 PM
Hi Tore,
Unfortunately not right now: AppendAttribute() wraps the version of the ObjectARX function that requires a db-resident blockref.
I'm hopeful that this will be enabled in a future release, but for now you're stuck with the approach I've shown here, I'm afraid.
Regards,
Kean
Posted by: Kean | May 25, 2007 at 01:57 PM
Hi Kean,
How do I run this code when a button on a form is clicked?
Thanks
shers
Posted by: shers | September 28, 2008 at 10:34 AM
Hi Shers,
It depends on whether you want to create a modal or modeless dialog. There should be examples on the ObjectARX SDK (under samples/dotNet) that show how to create dialogs and put code behind buttons.
Regards,
Kean
Posted by: Kean | September 29, 2008 at 09:31 AM
I tried to start really simple and just insert one block at (0,0) so that I could see it. I am getting the msg printed "(2126748928)Temperature Switch - Imperial inserted" on my command line but do not see the block (which I took from AutoCAD 2009/Samples/Dynamic Blocks)
Anyone have any ideas on why this code will not work. Block is loaded into the block table.
Thanks in advance
using System;
using Autodesk.AutoCAD;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;
using System.IO;
namespace BlockImport
{
public class BlockImportClass
{
[CommandMethod("BI")]
public void InsertBlock()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
//Test block name and point (final version will use user input)
string nameOfBlock = "Temperature Switch - Imperial";
Autodesk.AutoCAD.Geometry.Point3d pos = new Point3d(0, 0, 0);
// Get the current working database
Transaction tr = db.TransactionManager.StartTransaction();
// Then open the block table and check the
// block definition exists
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
if (!bt.Has(nameOfBlock))
{
ed.WriteMessage("\nBlock not found.");
}
else
{
ObjectId bdId = bt[nameOfBlock];
Point3d pt = new Point3d(0, 0, 0);
// Create the block reference
BlockReference br = new BlockReference(pt, bdId);
//Add block reference to model space table
BlockTableRecord btrms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
btrms.AppendEntity(br);
tr.AddNewlyCreatedDBObject(br, true);
//Graphics Flush so block will be visible
doc.TransactionManager.QueueForGraphicsFlush();
ed.WriteMessage(bdId + nameOfBlock + " inserted");
}
//Clean up transaction
tr.Commit();
tr.Dispose();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage("\nError during copy: " + ex.Message);
}
}
}
}
Posted by: Paul | November 26, 2008 at 01:57 AM
looks like it may be a problem with dynamic blocks? I just tested it with a standard block and had no trouble. Any ideas on how to use it for dynamic blocks?
Maybe just posting my code was enough therapy to try a new approach haha
Posted by: Paul | November 26, 2008 at 02:32 AM
It should work fine for dynamic blocks, too (just scanning your code). Please submit your question via the ADN site (if you're a member), or on the AutoCAD .NET Discussion Group. Someone there will be able to help.
Kean
Posted by: Kean | November 26, 2008 at 06:56 AM