September 2014

Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        








« January’s Plugin of the Month live on Autodesk Labs: DrawOrder By Layer for AutoCAD | Main | Combining AutoCAD blocks in separate files into a single DWG using .NET – Take 2 »

January 07, 2011

Combining AutoCAD blocks in separate files into a single DWG using .NET

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.

Exported blocks as separate DWG files

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:

Our blocks recombined into a single library

 

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.

blog comments powered by Disqus

Feed/Share

10 Random Posts