Get properties and place them into an attribute block

hmsnake
Participant
Participant

Get properties and place them into an attribute block

hmsnake
Participant
Participant

Hello, I am attacking a very ambitious project right now. I want to create a C# (.NET) command that:
1. I want to insert an attribute block into a layout. The user should be able to choose a DWG file that the attribute lives in with the file explorer. Give the user the option to insert into the current layout or into every layout within a SSM

2. I want it to get all of the properties from the layout and the SSM it is part of.

3. The name of each property (i.e. Totsheet) matches corresponds to a prompt in the attribute block. It should identify the prompt name and then insert the property gotten:

 

 


I started by trying to achieve each goal individually (see below) and they work. They are slightly simpler versions, but I figured I could ramp them up when the time came. I used Audrey Bushman's code to access the SSM because I could not figure it out myself. I am using ChatGPT to help as well since I am not familiar with the API.

My problems are:
1. I cant get the layout properties, only the SSM properties.
2. I can replace things within an attribute, but not the way I want to. The code that I've pieced together takes a string in from the user, then replaces all of the attributes with that string. I havent figured out any other attribute replacements that will work.

if this is impossible i will be sad but not surprised.. thank you all in advance

0 Likes
Reply
653 Views
8 Replies
Replies (8)

ed57gmc
Mentor
Mentor

Please post  your code to a code window.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes

hmsnake
Participant
Participant
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System.Windows.Forms;

public class InsertExternalBlock
{
    [CommandMethod("InsertExtBlock")]
    public void InsertBlockFromExternalFile()
    {
        // Get the current document and editor
        Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
        Editor ed = doc.Editor;

        // Prompt user to select a DWG file to insert as a block
        OpenFileDialog openFileDialog = new OpenFileDialog
        {
            Filter = "DWG Files (*.dwg)|*.dwg",
            Title = "Select the DWG File to Insert as a Block"
        };

        if (openFileDialog.ShowDialog() != DialogResult.OK)
        {
            ed.WriteMessage("\nOperation cancelled.");
            return;
        }

        string dwgFilePath = openFileDialog.FileName;

        // Run the AutoCAD INSERT command to insert the DWG as a block
        ed.Command("_.-INSERT", dwgFilePath, "0,0,0", "1", "1", "0");
    }
}
using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

[assembly: CommandClass(typeof(YourNamespace.UpdateBlockAttributesCommand))]

namespace YourNamespace
{
    public class UpdateBlockAttributesCommand : IExtensionApplication
    {
        public void Initialize() { }

        public void Terminate() { }

        [CommandMethod("UpdateBlockAttributes")]
        public void UpdateBlockAttributes()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            // Prompt user to enter the string
            PromptStringOptions promptStringOptions = new PromptStringOptions("\nEnter the string to insert into the block attributes: ");
            PromptResult promptResult = ed.GetString(promptStringOptions);

            if (promptResult.Status != PromptStatus.OK)
            {
                ed.WriteMessage("\nOperation canceled.");
                return;
            }

            string userInput = promptResult.StringResult;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // Iterate through all block references in the drawing
                BlockTable blockTable = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

                foreach (ObjectId blockId in blockTable)
                {
                    BlockTableRecord blockRecord = (BlockTableRecord)tr.GetObject(blockId, OpenMode.ForRead);

                    // Check if block name contains "Attrib"
                    if (blockRecord.Name.Contains("Attrib"))
                    {
                        // Iterate through all block references in the current space (ModelSpace or PaperSpace)
                        BlockTableRecord currentSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

                        foreach (ObjectId entityId in currentSpace)
                        {
                            Entity entity = (Entity)tr.GetObject(entityId, OpenMode.ForRead);

                            if (entity is BlockReference blockRef)
                            {
                                // Check if the block reference matches the desired block
                                if (blockRef.Name.Equals(blockRecord.Name, StringComparison.OrdinalIgnoreCase))
                                {
                                    foreach (ObjectId attId in blockRef.AttributeCollection)
                                    {
                                        AttributeReference attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForWrite);
                                        attRef.TextString = userInput;
                                    }
                                }
                            }
                        }
                    }
                }

                tr.Commit();
            }

            ed.WriteMessage("\nAttributes updated for blocks with 'Attrib' in their name.");
        }
    }
}
using System;
using System.Text;
using System.Windows.Forms; // For MessageBox
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using App = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rtm = Autodesk.AutoCAD.Runtime;
using Comp = ACSMCOMPONENTS24Lib;
using Autodesk.Civil.DatabaseServices;

[assembly: Rtm.ExtensionApplication(typeof(Bushman.AutoCAD.SheetSetTools.SheetSetCommands))]
[assembly: Rtm.CommandClass(typeof(Bushman.AutoCAD.SheetSetTools.SheetSetCommands))]

namespace Bushman.AutoCAD.SheetSetTools
{
    public class SheetSetCommands : Rtm.IExtensionApplication
    {
        const string ns = "bush"; // namespace

        [Rtm.CommandMethod(ns, "ss-get-properties", Rtm.CommandFlags.Modal)]
        public void GetSheetSetProperties()
        {
            App.Document doc = cad.DocumentManager.MdiActiveDocument;
            Db.Database db = doc.Database;
            Ed.Editor ed = doc.Editor;
            Comp.AcSmSheetSetMgr mng = new Comp.AcSmSheetSetMgr();
            Comp.IAcSmEnumDatabase enumerator = mng.GetDatabaseEnumerator();
            enumerator.Reset();
            Comp.AcSmDatabase smDb = enumerator.Next();

            if (smDb != null)
            {
                try
                {
                    smDb.LockDb(db);
                    Comp.AcSmSheetSet sheetset = smDb.GetSheetSet();

                    if (sheetset != null)
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.AppendLine($"Sheet Set Name: {sheetset.GetName()}");
                        sb.AppendLine($"Description: {sheetset.GetDesc()}");

                        // Retrieve custom properties
                        Comp.AcSmCustomPropertyBag customProperties = sheetset.GetCustomPropertyBag();
                        if (customProperties != null)
                        {
                            // List of desired custom property names
                            string[] desiredProperties = new string[]
                            {
                                "Project Name",
                                "Project Number",
                                "Issued Date:",
                                "Title1",
                                "Title2",
                                "Title3",
                                "TotSheets"
                            };

                            // Iterate through desired properties
                            foreach (var propertyName in desiredProperties)
                            {
                                try
                                {
                                    var property = customProperties.GetProperty(propertyName) as Comp.AcSmCustomPropertyValue;
                                    if (property != null)
                                    {
                                        sb.AppendLine($"{propertyName}: {property.GetValue()}");
                                    }
                                    else
                                    {
                                        sb.AppendLine($"{propertyName}: [Not Found]");
                                    }
                                }
                                catch (Exception ex)
                                {
                                    sb.AppendLine($"{propertyName}: Error retrieving value - {ex.Message}");
                                }
                            }
                        }
                        else
                        {
                            sb.AppendLine("No custom properties found.");
                        }

                        // Display the properties in a message box
                        MessageBox.Show(sb.ToString(), "Sheet Set Properties");
                    }
                    else
                    {
                        ed.WriteMessage("\nNo sheet set is available.");
                    }
                }
                catch (Exception ex)
                {
                    ed.WriteMessage($"\nError: {ex.Message}");
                }
                finally
                {
                    smDb.UnlockDb(db, true);
                }
            }
            else
            {
                ed.WriteMessage("\nNo sheet set is open.");
            }
        }

        private string GetCustomPropertyValue(Comp.AcSmCustomPropertyBag customProperties, string propertyName)
        {
            try
            {
                var property = customProperties.GetProperty(propertyName) as Comp.AcSmCustomPropertyValue;
                if (property != null)
                {
                    return property.GetValue();
                }
            }
            catch (Exception ex)
            {
                // Log or handle the exception if needed
                Console.WriteLine($"Error retrieving custom property {propertyName} - {ex.Message}");
            }
            return null;
        }

        public void Initialize()
        {
            App.Document doc = cad.DocumentManager.MdiActiveDocument;
            Ed.Editor ed = doc.Editor;
            ed.WriteMessage("\nSheetSetTools. © Andrey Bushman, 2013\n\n");
        }

        public void Terminate()
        {
            // Empty body.
        }
    }
}
0 Likes

ed57gmc
Mentor
Mentor

If I get more time, I might be able to add more, but here's an extension method that will help.

BTW, I think it's Andrey, not Audrey.


        //InsertBlockReference
        /// <summary>
        /// Insert a block into the current space. You can optionally fill attribute values.
        /// </summary>
        /// <param name="target">The BlockTableRecord of the (model/paper) space to insert the block in.</param>
        /// <param name="blkName">A string of the block's name.</param>
        /// <param name="insertPoint">A Point3d to be used as the insertion point.</param>
        /// <param name="attValues">A Dictionary&gtstring, string&lt in the form of Key, value. 
        /// To be used to supply tag string values. If a key is not found for an attribute, 
        /// the value from the AttributeDefinition is used.</param>
        /// <returns><see cref="Autodesk.AutoCAD.DatabaseServices.BlockReference"/> of the inserted block.</returns>
        public static BlockReference InsertBlockReference(
                                        this BlockTableRecord target,
                                        string blkName,
                                        Point3d insertPoint,
                                        Dictionary<string, string> attValues = null)
        {
            if (target == null)
                throw new ArgumentNullException("target");

            Database db = target.Database;
            Transaction tr = db.TransactionManager.TopTransaction ?? throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
            BlockReference br = null;
            BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

            ObjectId btrId = bt.GetBlock(blkName);

            if (btrId != ObjectId.Null)
            {
                br = new BlockReference(insertPoint, btrId);
                _ = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
                target.AppendEntity(br);
                tr.AddNewlyCreatedDBObject(br, true);

                br.AddAttributeReferences(attValues);
            }
            return br;
        }

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes

hmsnake
Participant
Participant

Oh my gosh you are so right🖐 I will play around with this, thank you! If you have any information on accessing layout properties (within the same sheet set manage) that would be much appreciated as well!

0 Likes

ActivistInvestor
Advisor
Advisor

@ed57gmc wrote:

If I get more time, I might be able to add more, but here's an extension method that will help.

BTW, I think it's Andrey, not Audrey.

 

                br.AddAttributeReferences(attValues);

 


That's not a method of BlockReference, so it must be an an extension method that comes from the same source as the rest of the code.

0 Likes

ed57gmc
Mentor
Mentor

@ActivistInvestor wrote:

@ed57gmc wrote:

If I get more time, I might be able to add more, but here's an extension method that will help.

BTW, I think it's Andrey, not Audrey.

 

                br.AddAttributeReferences(attValues);

 


That's not a method of BlockReference, so it must be an an extension method that comes from the same source as the rest of the code.


Yes, thanks.

 


        /// <summary>
        /// Extension method that returns adds <see cref="AttributeReference"/>'s to a <see cref="BlockReference"/>.
        /// AttributeReference values are taken from the Dictionary attValues argument.
        /// </summary>
        /// <param name="target">A BlockReference to add the AttributeReferences to.</param>
        /// <param name="attValues">The Dictionary to get attribute values from. 
        /// The first string is the Key and is equal to the AttributeDefinition.Tagstring.
        /// The second string is the attribute value.</param>
        /// <returns>Void.</returns>
        public static void AddAttributeReferences(this BlockReference target, Dictionary<string, string> attValues)
        {
            if (target == null)
                throw new ArgumentNullException("target");

            Transaction tr = target.Database.TransactionManager.TopTransaction ?? throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(target.BlockTableRecord, OpenMode.ForRead);

            foreach (AttributeDefinition attDef in btr.AttributeDefinitions(tr))
            {
                if (!attDef.Constant)
                {
                    AttributeReference attRef = new AttributeReference();
                    attRef.SetAttributeFromBlock(attDef, target.BlockTransform);
                    if (attValues != null && attValues.ContainsKey(attDef.Tag.ToUpper()))
                    {
                        attRef.TextString = attValues[attDef.Tag.ToUpper()];
                    }
                    target.AttributeCollection.AppendAttribute(attRef);
                    tr.AddNewlyCreatedDBObject(attRef, true);
                }
            }
        }


        /// <summary>
        /// Extension method that returns a <see cref="Collection"/> of <see cref="AttributeDefinition"/>'s from a <see cref="BlockTableRecord"/>.
        /// </summary>
        /// <param name="btr">This BlockTableRecord to use.</param>
        /// <param name="trans">The Transaction within which to pass the Collection of AttributeDefinitions.</param>
        /// <returns>A Collection of AttributeDefinitions.</returns>
        public static Collection<AttributeDefinition> AttributeDefinitions(this BlockTableRecord btr, Transaction trans)
        {
            Collection<AttributeDefinition> atts = new Collection<AttributeDefinition>();

            RXClass theClass = RXObject.GetClass(typeof(AttributeDefinition));

            // Loop through the entities in model space
            foreach (ObjectId objectId in btr.Cast<ObjectId>())
            {
                // Look for entities of the correct type
                if (objectId.ObjectClass.IsDerivedFrom(theClass))
                {
                    AttributeDefinition attd = objectId.OpenAs<AttributeDefinition>(trans);
                    atts.Add(attd);
                }
            }
            return atts;
        }

 

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes

ActivistInvestor
Advisor
Advisor

The reason I put code on GitHub is because of this very problem - I can't afford to take the time to find and add all of the code which the code I'm sharing is dependent on, and in some cases, the dependencies can be half-a-dozen layers deep.

 

I don't write code that can exist in a vacuum (the way coding examples are written), because that's totally unacceptable for real-world application development . So, sharing snippets of code or individual methods is very difficult, as very little of the code I've written over the years can exist or work in a vacuum, without any dependence on other custom-developed code.

 

 

btr.AttributeDefinitions(tr)

 

 

If you're sharing your code, you can post it in a GitHub repository along with all of the code it's dependent on, and then just share a link to it, and you don't need to worry about finding out and adding what's missing.

0 Likes

ed57gmc
Mentor
Mentor

Yes, that's a good idea. I've only recently gotten access to GitHub here at work and I've been starting to do that.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes