1

I'm trying to update the values of some attributes in the paperspace. This block with its attributes is found on multiple layouts. I have tried the code posted below but I'm getting an NullReferenceException from the AttributeCollection. The block is inserted in the drawing layout. Can someone point me in the right direction how to fix this or what I'm doing wrong? Thanks in advance!

namespace InvullenKleinTitelhoek
    {
        public class Class2
        {
            [CommandMethod("KleineTitelhoekbis")]
            public void UpdateBlockAttributes()
            {
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Database db = doc.Database;
                Editor edt = doc.Editor;
        
                //To change 
                //blockname     TBM_DATA04_S_N
                //att           AT_DATA_DESC3_S_N
                //att           AT_DATA_DESC4_S_N"




                //-----------------------------------------------------------------//

                PromptStringOptions lijnDrie = new PromptStringOptions("Lijn 3");
                lijnDrie.AllowSpaces = true;

                PromptResult textStringLijnDrie = edt.GetString(lijnDrie);
                string stLijnDrie = textStringLijnDrie.StringResult;

                PromptStringOptions lijnVier = new PromptStringOptions("Lijn 4");
                lijnVier.AllowSpaces = true;

                PromptResult textStringLijnVier = edt.GetString(lijnVier);
                string stLijnVier = textStringLijnVier.StringResult;


                //-----------------------------------------------------------------//


                using (Transaction trans = db.TransactionManager.StartTransaction())
                {
                    try
                    {
                        DBDictionary layoutDict = trans.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
                
                        foreach(DBDictionaryEntry entry in layoutDict)
                        {
                            Layout ltr = (Layout)trans.GetObject((ObjectId)entry.Value, OpenMode.ForWrite);

                            BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                            BlockTableRecord btr = trans.GetObject(bt[BlockTableRecord.PaperSpace], OpenMode.ForRead) as BlockTableRecord;

                            foreach (ObjectId id in btr)
                            {
                                DBObject obj = trans.GetObject(id, OpenMode.ForRead);
                                BlockReference br = obj as BlockReference;

                                AttributeCollection attCol = br.AttributeCollection;

                                foreach (ObjectId objId in attCol)
                                {
                                    DBObject dbobj = trans.GetObject(objId, OpenMode.ForRead) as DBObject;

                                    AttributeReference attRef = dbobj as AttributeReference;



                                    if (attRef.Tag == "AT_DATA_DESC3_S_N")
                                    {
                                        attRef.UpgradeOpen();
                                        attRef.TextString = stLijnDrie;
                                
                                    }

                                    if (attRef.Tag == "AT_DATA_DESC4_S_N")
                                    {
                                        attRef.UpgradeOpen();
                                        attRef.TextString = stLijnVier;

                                    }


                                }
                        
                            }

                            trans.Commit();

                        }

                




                    }

                    catch (System.Exception ex)
                    {
                        edt.WriteMessage("error:  " + ex.Message);
                    }

                }

            }
        }
    }
Pluribus
  • 15
  • 2

1 Answers1

0

good question

The code had some bugs

  1. need to check for null attribute collections
  2. need to handle a scenario where objects other than blocks are on the layout (cast to block will fail)
  3. need to move the trans.Commit statement outside of all the loops

Here's the updated code.
One thing to note, this code will only update blocks on the currently active paperspace tab when the command starts.
Even though it loops over all layout tabs, it will not update all layouts.

        public void UpdateBlockAttributes()
    {
        Document doc = Application.DocumentManager.MdiActiveDocument;
        Database db = doc.Database;
        Editor edt = doc.Editor;
        PromptStringOptions lijnDrie = new PromptStringOptions("Lijn 3");
        lijnDrie.AllowSpaces = true;
        PromptResult textStringLijnDrie = edt.GetString(lijnDrie);
        string stLijnDrie = textStringLijnDrie.StringResult;
        PromptStringOptions lijnVier = new PromptStringOptions("Lijn 4");
        lijnVier.AllowSpaces = true;
        PromptResult textStringLijnVier = edt.GetString(lijnVier);
        string stLijnVier = textStringLijnVier.StringResult;

        // The document must be locked before making modifications
        using (DocumentLock @lock = doc.LockDocument)
        {
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                try
                {
                    DBDictionary layoutDict = trans.GetObject(db.LayoutDictionaryId, OpenMode.ForWrite) as DBDictionary;
                    string originalLayout = LayoutManager.Current.CurrentLayout;
                    foreach (DBDictionaryEntry entry in layoutDict)
                    {
                        Layout ltr = (Layout)trans.GetObject((ObjectId)entry.Value, OpenMode.ForWrite);
                        string layoutName = ltr.LayoutName;
                        BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
                        BlockTableRecord btr = trans.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForWrite) as BlockTableRecord;
                        if (!layoutName.Equals("Model"))
                        {
                            // Set layout as current so code can modify block attributes on the layout
                            LayoutManager.Current.CurrentLayout = layoutName;
                            edt.WriteMessage("layout name:  " + layoutName + Constants.vbLf);
                            foreach (ObjectId id in btr)
                            {
                                DBObject obj = trans.GetObject(id, OpenMode.ForWrite);
                                Entity entity = obj as Entity;
                                string ownerName = entity.BlockName;
                                BlockReference br = obj as BlockReference;
                                // If cast to block succeeded
                                if (br != null)
                                {
                                    string blockName = br.Name;
                                    AttributeCollection attCol = br.AttributeCollection;
                                    edt.WriteMessage("block name:  " + blockName + Constants.vbLf);
                                    // If block has attributes
                                    if (attCol != null & attCol.Count > 0)
                                    {
                                        foreach (ObjectId objId in attCol)
                                        {
                                            DBObject dbobj = trans.GetObject(objId, OpenMode.ForWrite) as DBObject;
                                            AttributeReference attRef = dbobj as AttributeReference;

                                            if (attRef.Tag == "AT_DATA_DESC3_S_N")
                                            {
                                                edt.WriteMessage("edited att value for:  " + attRef.Tag + Constants.vbLf);
                                                attRef.UpgradeOpen();
                                                attRef.TextString = stLijnDrie;
                                            }

                                            if (attRef.Tag == "AT_DATA_DESC4_S_N")
                                            {
                                                edt.WriteMessage("edited att value for:  " + attRef.Tag + Constants.vbLf);
                                                attRef.UpgradeOpen();
                                                attRef.TextString = stLijnVier;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    LayoutManager.Current.CurrentLayout = originalLayout;
                    // Transaction needs to be outside of loop where code is doing modifications, otherwise it will be closed on commit
                    // causing error mesasge "Operation is not valid due to the current state of the object."
                    trans.Commit();
                }
                catch (Exception ex)
                {
                    edt.WriteMessage("error:  " + ex.Message + Constants.vbLf);
                }
            }
        }
    }
Dharman
  • 30,962
  • 25
  • 85
  • 135
cadwiki.net
  • 68
  • 1
  • 5
  • 1
    Thank you for replaying. Just a few more questions (I'm new to world of c#) The check for the null attribute collection is that something which is necessary or just good practice? Why does the document has to locked? How to update all the layouts is a bit unclear to me? And the last one what is the "Constants.vbLf" thing? Thanks in advance! – Pluribus Feb 07 '22 at 07:23
  • It's always good practice to check for nulls when you are going to attempt to access an object (like the attribute collection) that might be null. The document has to be locked anytime you are going to make changes to it. This prevents the user from deleting something that the code is going to modify. vbLf = visualBasicLinefeed. https://stackoverflow.com/questions/27223228/differences-between-vblf-vbcrlf-vbcr-constants – cadwiki.net Feb 07 '22 at 15:02