Important note: the code in this post – while potentially interesting, at a certain level – has been superceded by the code in the following post. Please go ahead and use that simpler, more complete implementation instead.
This question came in, over the holidays, that seemed like it was worth addressing:
How do I get an external DWG into the blocks table as a block definition?
I started by quoting this ancient (at least in terms of this blog) post, but it turned out only to be of partial help: the problem is that the external DWG does not contain the block, it is the block. Which is pretty common – the WBLOCK command allows you to export blocks to individual DWG files, and INSERT clearly allows you to bring them back in.
So how would you go about combining a set of these externally-defined blocks into a single library?
Let’s think this through, a little. The blocks in these external drawings are actually not defined as blocks – as such – but have their contents in the modelspace. So one way to do this would be to adjust the approach in the old post (and it really does look old now, to me – which hopefully means I’ve learned something over the last four years ;-) to use WblockCloneObjects() to bring across the modelspace from each of the source drawings into the block table of our target database (which, for simplicity’s sake, we’ll probably just take as that of the active drawing).
A few things to consider:
- The blocks will be brought it with names such as “*Model_Space0”, “*Model_Space1”, etc., so we will need to remap them
- Apart from anything else, leaving them like this will make them anonymous and harder to use
- We should set the new name to be that of the drawing file
- This is the default filename used when WBLOCKing to a DWG
Here’s the updated C# code to all the blocks defined in a particular folder into the active drawing:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System.IO;
using System;
namespace BlockImport
{
public class BlockImportClass
{
[CommandMethod("CBL")]
public void CombineBlocksIntoLibrary()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Database destDb = doc.Database;
// Get name of folder from which to load and import blocks
PromptResult pr =
ed.GetString("\nEnter the folder of source drawings: ");
if (pr.Status != PromptStatus.OK)
return;
string pathName = pr.StringResult;
// Check the folder exists
if (!Directory.Exists(pathName))
{
ed.WriteMessage(
"\nDirectory does not exist: {0}", pathName
);
return;
}
// Get the names of our DWG files in that folder
string[] fileNames = Directory.GetFiles(pathName, "*.dwg");
// A counter for the files we've imported
int imported = 0, failed = 0;
// For each file in our list
foreach (string fileName in fileNames)
{
// Double-check we have a DWG file (probably unnecessary)
if (fileName.EndsWith(
".dwg",
StringComparison.InvariantCultureIgnoreCase
)
)
{
// Catch exceptions at the file level to allow skipping
try
{
// Create a source database to load the DWG into
using (Database db = new Database(false, true))
{
// Read the DWG into our side database
db.ReadDwgFile(fileName, FileShare.Read, true, "");
// Create a list of block identifiers (will only
// contain one entry, the modelspace ObjectId)
ObjectIdCollection ids = new ObjectIdCollection();
// Start a transaction on the source database
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// Open the block table
BlockTable bt =
(BlockTable)tr.GetObject(
db.BlockTableId,
OpenMode.ForRead
);
// Add modelspace to list of blocks to import
ids.Add(bt[BlockTableRecord.ModelSpace]);
// Committing is cheaper than aborting
tr.Commit();
}
// Copy our modelspace block from the source to
// destination database
// (will also copy required, referenced objects)
IdMapping im = new IdMapping();
db.WblockCloneObjects(
ids,
destDb.BlockTableId,
im,
DuplicateRecordCloning.MangleName,
false
);
// Start a transaction on the destination database
Transaction tr2 =
destDb.TransactionManager.StartTransaction();
using (tr2)
{
// Work through the results of the WblockClone
foreach (IdPair ip in im)
{
// Open each new destination object, checking for
// BlockTableRecords
BlockTableRecord btr =
tr2.GetObject(ip.Value, OpenMode.ForRead)
as BlockTableRecord;
if (btr != null)
{
// If the name starts with the modelspace string
if (
btr.Name.StartsWith(
BlockTableRecord.ModelSpace,
StringComparison.InvariantCultureIgnoreCase
)
)
{
// Get write access to it and change the name
// to that of the source drawing
btr.UpgradeOpen();
btr.Name =
Path.GetFileNameWithoutExtension(fileName);
}
}
}
// We need to commit, as we've made changes
tr2.Commit();
}
// Print message and increment imported block counter
ed.WriteMessage("\nImported from \"{0}\".", fileName);
imported++;
}
}
catch (System.Exception ex)
{
ed.WriteMessage(
"\nProblem importing \"{0}\": {1} - file skipped.",
fileName, ex.Message
);
failed++;
}
}
}
ed.WriteMessage(
"\nImported block definitions from {0} files{1} in " +
"\"{2}\" into the current drawing.",
imported,
failed > 0 ? " (" + failed + " failed)" : "",
pathName
);
}
}
}
To test the code, I went ahead and WBLOCKed the various blocks contained in the “Dynamic Blocks\Architectural - Metric” sample file into the “C:\Blocks” folder on my local system.
Running the CBL command, we see this output on the command-line:
Command: CBL
Enter the folder of source drawings: C:\Blocks
Imported from "C:\Blocks\Aluminum Window (Elevation) - Metric.dwg".
Imported from "C:\Blocks\Door - Metric.dwg".
Imported from "C:\Blocks\Door Elevation - Metric.dwg".
Imported from "C:\Blocks\Fluorescent (Recessed) - Metric.dwg".
Imported from "C:\Blocks\Stud - Metric.dwg".
Imported from "C:\Blocks\Toilet - Metric.dwg".
Imported from "C:\Blocks\Trees - Metric.dwg".
Imported from "C:\Blocks\Vehicles - Metric.dwg".
Imported from "C:\Blocks\Window - Metric.dwg".
Imported block definitions from 9 files in "C:\Blocks" into the current drawing.
If we then run the INSERT command, we see our blocks in the current drawing, along with any associated blocks upon which they rely:
Update
I’ve been reminded by comments that I’ve missed something obvious (using Database.Insert() to avoid some of the messiness).
I’ll take another look at this after the weekend and post another update.
Thanks for keeping me honest! :-)
Update 2
As now mentioned at the top of this post, the next post contains a simpler, more complete implementation of this technique.