Kean Walmsley


  • About the Author
    Kean on Google+

July 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 31    








« Coming in September: Autodesk DevCamps - Customization and Application Development Conferences | Main | Updating a specific attribute inside a folder of AutoCAD drawings using .NET »

July 23, 2007

Updating a specific attribute inside an AutoCAD drawing using .NET

This suggestion came up in reference to this previous post about using side databases. The request is to be able to open a number of DWG files and modify a particular attribute, saving the files back.

Rather than jumping in and solving both problems in one post, we'll start today with the problem of updating the attribute and then in the next post we'll look at some code we can use to process a folder of DWGs, opening, updating and saving each one. I'll probably then go one step further and look at the steps needed to extract this code and make it work in RealDWG (for which you will need to have the toolkit installed, of course).

This is actually the classic way to develop RealDWG applications - work on the code inside AutoCAD, relying only on classes that are available in RealDWG, such as Database - and, once tested, build the code into a RealDWG application. AutoCAD is the perfect UI and test harness for RealDWG code. :-)

To solve the problem of updating a particular attribute in a drawing, I started by copying a bunch of code from this post, as it handled the recursive iteration of blocks. I created a primary function (UpdateAttributesInDatabase()) that takes a Database parameter - as this will facilitate the future calling of the code on a number of drawings - and added some code to prompt the user for the name of the block and attribute to modify, and of course the new value to which we will set the attribute.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;


namespace BlockIterator

{

  public class Commands

  {

    [CommandMethod("UA")]

    public void UpdateAttribute()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;


      // Have the user choose the block and attribute

      // names, and the new attribute value


      PromptResult pr =

        ed.GetString(

          "\nEnter name of block to search for: "

        );

      if (pr.Status != PromptStatus.OK)

        return;

      string blockName = pr.StringResult.ToUpper();


      pr =

        ed.GetString(

          "\nEnter tag of attribute to update: "

        );

      if (pr.Status != PromptStatus.OK)

        return;

      string attbName = pr.StringResult.ToUpper();


      pr =

        ed.GetString(

          "\nEnter new value for attribute: "

        );

      if (pr.Status != PromptStatus.OK)

        return;

      string attbValue = pr.StringResult;


      UpdateAttributesInDatabase(

        db,

        blockName,

        attbName,

        attbValue

      );

    }


    private void UpdateAttributesInDatabase(

      Database db,

      string blockName,

      string attbName,

      string attbValue

    )

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;


      // Get the IDs of the spaces we want to process

      // and simply call a function to process each


      ObjectId msId, psId;

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        BlockTable bt =

          (BlockTable)tr.GetObject(

            db.BlockTableId,

            OpenMode.ForRead

          );

        msId =

          bt[BlockTableRecord.ModelSpace];

        psId =

          bt[BlockTableRecord.PaperSpace];


        // Not needed, but quicker than aborting

        tr.Commit();

      }

      int msCount =

        UpdateAttributesInBlock(

          msId,

          blockName,

          attbName,

          attbValue

        );

      int psCount =

        UpdateAttributesInBlock(

          psId,

          blockName,

          attbName,

          attbValue

        );

      ed.Regen();


      // Display the results


      ed.WriteMessage(

        "\nProcessing file: " + db.Filename

      );

      ed.WriteMessage(

        "\nUpdated {0} instance{1} of " +

        "attribute {2} in the modelspace.",

        msCount,

        msCount == 1 ? "" : "s",

        attbName

      );

      ed.WriteMessage(

        "\nUpdated {0} instance{1} of " +

        "attribute {2} in the default paperspace.",

        psCount,

        psCount == 1 ? "" : "s",

        attbName

      );

    }


    private int UpdateAttributesInBlock(

      ObjectId btrId,

      string blockName,

      string attbName,

      string attbValue

    )

    {

      // Will return the number of attributes modified


      int changedCount = 0;

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;


      Transaction tr =

        doc.TransactionManager.StartTransaction();

      using (tr)

      {

        BlockTableRecord btr =

          (BlockTableRecord)tr.GetObject(

            btrId,

            OpenMode.ForRead

          );


        // Test each entity in the container...


        foreach (ObjectId entId in btr)

        {

          Entity ent =

            tr.GetObject(entId, OpenMode.ForRead)

            as Entity;


          if (ent != null)

          {

            BlockReference br = ent as BlockReference;

            if (br != null)

            {

              BlockTableRecord bd =

                (BlockTableRecord)tr.GetObject(

                  br.BlockTableRecord,

                  OpenMode.ForRead

              );


              // ... to see whether it's a block with

              // the name we're after


              if (bd.Name.ToUpper() == blockName)

              {


                // Check each of the attributes...


                foreach (

                  ObjectId arId in br.AttributeCollection

                )

                {

                  DBObject obj =

                    tr.GetObject(

                      arId,

                      OpenMode.ForRead

                    );

                  AttributeReference ar =

                    obj as AttributeReference;

                  if (ar != null)

                  {

                    // ... to see whether it has

                    // the tag we're after


                    if (ar.Tag.ToUpper() == attbName)

                    {

                      // If so, update the value

                      // and increment the counter


                      ar.UpgradeOpen();

                      ar.TextString = attbValue;

                      ar.DowngradeOpen();

                      changedCount++;

                    }

                  }

                }

              }


              // Recurse for nested blocks

              changedCount +=

                UpdateAttributesInBlock(

                  br.BlockTableRecord,

                  blockName,

                  attbName,

                  attbValue

                );

            }

          }

        }

        tr.Commit();

      }

      return changedCount;

    }

  }

}

I tested this code by adding a block called TEST to the modelspace and to the primary layout, both at the top-level and nested within other blocks. This block contained three attributes, imaginatively named "ONE", "TWO" and "THREE". Here's the command-line output when I ran the code on this drawing, selecting the block TEST and changing attribute ONE to the value of 100:

Command: UA

Enter name of block to search for: TEST

Enter tag of attribute to update: ONE

Enter new value for attribute: 100

Regenerating model.

Processing file: C:\temp\attributes.dwg

Updated 3 instances of attribute ONE in the modelspace.

Updated 2 instances of attribute ONE in the default paperspace.

Next time we'll look at how to run this code on multiple DWG files without loading them into the drawing editor.

TrackBack

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

Listed below are links to weblogs that reference Updating a specific attribute inside an AutoCAD drawing using .NET:

blog comments powered by Disqus

10 Random Posts