Kean Walmsley

July 2009

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 31  

Twitter Updates

    follow me on Twitter



    « Handling Automation errors in Visual LISP | Main | Breaking it down - a closer look at the C# code for importing blocks »

    August 18, 2006

    Import blocks from an external DWG file using .NET

    We're going to use a "side database" - a drawing that is loaded in memory, but not into the AutoCAD editor - to import the blocks from another drawing into the one active in the editor.

    Here's some C# code. The inline comments describe what is being done along the way. Incidentally, the code could very easily be converted into a RealDWG application that works outside of AutoCAD (we would simply need to change the destDb from the MdiActiveDocument's Database to the HostApplicationServices' WorkingDatabase, and use a different user interface for getting/presenting strings from/to the user).

    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;


    namespace BlockImport

    {

      public class BlockImportClass

      {

        [CommandMethod("IB")]

        public void ImportBlocks()

        {

          DocumentCollection dm =

              Application.DocumentManager;

          Editor ed = dm.MdiActiveDocument.Editor;

          Database destDb = dm.MdiActiveDocument.Database;

          Database sourceDb = new Database(false, true);

          PromptResult sourceFileName;

          try

          {

            // Get name of DWG from which to copy blocks

            sourceFileName =

              ed.GetString("\nEnter the name of the source drawing: ");

            // Read the DWG into a side database

            sourceDb.ReadDwgFile(sourceFileName.StringResult,

                                System.IO.FileShare.Read,

                                true,

                                "");


            // Create a variable to store the list of block identifiers

            ObjectIdCollection blockIds = new ObjectIdCollection();


            Autodesk.AutoCAD.DatabaseServices.TransactionManager tm =

              sourceDb.TransactionManager;


            using (Transaction myT = tm.StartTransaction())

            {

              // Open the block table

              BlockTable bt =

                  (BlockTable)tm.GetObject(sourceDb.BlockTableId,

                                          OpenMode.ForRead,

                                          false);


              // Check each block in the block table

              foreach (ObjectId btrId in bt)

              {

                BlockTableRecord btr =

                  (BlockTableRecord)tm.GetObject(btrId,

                                                OpenMode.ForRead,

                                                false);

                // Only add named & non-layout blocks to the copy list

                if (!btr.IsAnonymous && !btr.IsLayout)

                  blockIds.Add(btrId);

                btr.Dispose();

              }

            }

            // Copy blocks from source to destination database

            IdMapping mapping = new IdMapping();

            sourceDb.WblockCloneObjects(blockIds,

                                        destDb.BlockTableId,

                                        mapping,

                                        DuplicateRecordCloning.Replace,

                                        false);

            ed.WriteMessage("\nCopied "

                            + blockIds.Count.ToString()

                            + " block definitions from "

                            + sourceFileName.StringResult

                            + " to the current drawing.");

          }

          catch(Autodesk.AutoCAD.Runtime.Exception ex)

          {

              ed.WriteMessage("\nError during copy: " + ex.Message);

          }

          sourceDb.Dispose();

        }

      }

    }


    And that's all there is to it. More information on the various objects/properties/methods used can be found in the ObjectARX Reference.

    TrackBack

    TrackBack URL for this entry:
    http://www.typepad.com/services/trackback/6a00d83452464869e200d834df449d69e2

    Listed below are links to weblogs that reference Import blocks from an external DWG file using .NET:

    Comments

    With this update/override any existing blocks in the active dwg? If not how would you do so?

    Hi Barry,

    Yes - the key is in the 4th argument to WblockCloneObject(), which is DuplicateRecordCloning.Replace. If you don't want to overwrite/update existing blocks you can use DuplicateRecordCloning.Ignore.

    I'll post another entry next week to look into the code in a bit more detail - it was getting late on Friday night and I wanted to get the code posted before the weekend. There are a few details that are worth looking at a little closer.

    Kean

    Kean,

    I have found your blog to be very helpful. I was hoping to meet you at AU2006 but didn't have time to find everyone. Don't even know if you were there.

    Do you know if there is a way to copy a block that has sub blocks and create new blocks and sub blocks without the original blocks xdata? I'm trying to create a "dumb" copy of the original block without all the xdata to use in various other views.

    Hi Stuart,

    I wasn't able to make it to this year's AU, unfortunately. Perhaps next year.

    The way to go should be to go through the "mapping" object populated by WblockCloneObjects(), and post-process the various new blocks to dumb them down (removing their xdata).

    Regards,

    Kean

    Hi,

    I'm using this code in my application but I need to import also dynamic properties. How can I do it?
    Thanks

    Do you mean dynamic blocks? Please explain exactly what doesn't work in the code, and I'll take a look. I don't remember whether I tested this with dynamic blocks (I posted it quite some time ago).

    Kean

    Hi Kean. Yes I mean dynamic block.
    I have a dwg (Dest.dwg) with a static block called PB.
    I want to import a block called PB from an external file (Library.dwg).
    When I run "IB" command block in Dest.dwg the PB block is modified but it doesn't have rotation, visibility and other dynamic actions.
    Thanks

    Hi Stefano,

    I tested the IB command with one of the standard Dynamic Block libraries (in "C:\Program Files\Autodesk\AutoCAD 2008\Sample\Dynamic Blocks\Annotation - Metric.dwg" on my system), and it seemed to work fine. I could insert the imported Dynamic Blocks and they appeared to behave in a dynamic fashion. Can you test against the standard ones, and try to narrow down the specific problem?

    Regards,

    Kean

    I post this comment to notify readers that Kean and I understand that the problem with dynamic block resides in AutoCAD 2006. In AutoCAD 2008 it's possibile to redefine a block also with dynamic property. Thanks Kean!

    Kean,
    Thanks for this sample code. I've tried several different similar methods in VB.Net but it always ended with a fatal error. If you don't mind I would like to explain how I entend to use the code to see if you have a better suggestion. I have hundreds of Annotation blocks that I use for both imperial and metric drawings. I'm sure you're aware that if the block being inserted is an annotative block the insunits are ignored. I need to insert the blocks and scale them by both the annotative scale and the insunits. I would also like to put these blocks on toolpalletes. I've written the code to scale the blocks with both parameters and it works great. The only problem is I wrote in VB.Net. My skills are pretty limited with C#. My plan was to code the command macro on the toolpallete to set the UserS1 variable to the path of the source drawing and UserS2 to the name of the block. Then I would call the command to import and scale the block. I was able to tweak your code to read these variables and import the block. Now all I need to do is convert my VB.Net code to C# so I can insert the block. Could you possible give me a similar example of your C# code in VB? Or if you have any other suggestions I would really like to hear them.

    Thanks,
    Steve

    Steve,

    I'm sorry for the delay in responding. When converting between C# and VB, I usually use the tool I linked to by this post:

    http://through-the-interface.typepad.com/through_the_interface/2006/08/some_cool_copyp.html

    You can also use the online conversions tools directly that this AddIn connects to:

    http://www.carlosag.net/Tools/CodeTranslator
    http://www.kamalpatel.net/ConvertCSharp2VB.aspx

    Also, apparently SharpDevelop is a good tool for code conversion.

    Regards,

    Kean

    Is there a similar way for creating Block Definitions 'on the fly'? I need to go from a polyline entity to a block definition...

    Yes, you can certainly add a new BlockTableRecord to the BlockTable and use its ID as the destination for the WblockCloneObjects() call (passing in the ObjectIds of the objects to put in the block).

    Kean

    Am I mistaken or is this the .NET equivalent to the following line of VBA code:

    ThisDrawing.ModelSpace.InsertBlock(newCoord, "c:\blockName.dwg", xyzScale, xyzScale, xyzScale, 0)

    I'm confused by the amount of code needed to do what was done in VBA with 1 line. Am I missing something?

    Well, yes and no... InsertBlock() loads the DWG into a BlockTableRecord and inserts it into the space upon which it's called. The above code imports blocks from a DWG file so they can be inserted at the user's leisure.

    The COM API (to which VBA provides access) is higher-level than .NET - it's focus is automating tasks, while .NET (and ObjectARX, upon which it is very closely based) are more powerful APIs, yet sometimes require more code to do certain tasks.

    It's ultimately a balance between control and succinctness: with lower-level code you get much greater control but it often comes with a coding overhead.

    Kean

    Hi Kean,

    We have had advances in our project thanks to your blog. But now, we have found another problem. We have been able to import blocks and layer from a DWG to another, but we haven't been able to import the entities. Our project consist in import completly dwg into another dwg. Do you know if that is possible?
    With the objectARX can add new entities in a blockTableRecord, but can we do that from another dwg?

    Thanks,

    Omar

    Hi Omar,

    WblockCloneObjects() and Insert() should get you there.

    Please submit follow-up questions via ADN, if you're a member, or the Autodesk discussion groups, if not.

    Regards,

    Kean

    Verify your Comment

    Previewing your Comment

    This is only a preview. Your comment has not yet been posted.

    Working...
    Your comment could not be posted. Error type:
    Your comment has been posted. Post another comment

    The letters and numbers you entered did not match the image. Please try again.

    As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

    Having trouble reading this image? View an alternate.

    Working...

    Post a comment

    Feed & Share

    Search