Changing block attributes after using INSERT

Changing block attributes after using INSERT

Anonymous
Not applicable
1,186 Views
2 Replies
Message 1 of 3

Changing block attributes after using INSERT

Anonymous
Not applicable

I have an INSERT command in my code for a dynamic block because I want the user to place the block. I then use a selection filter to find the recently inserted block based on an attribute with a set default string. Once I find it I change the blocks attribute string to something else that I need.

Is there a way of getting the block before it is put into the database so I don't have to look for it to change its attributes?

I was a database event may do the trick?

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using app = Autodesk.AutoCAD.ApplicationServices.Application;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

namespace placePillars
{
    public class Class1
    {
        [CommandMethod("AddPlObjEvent")]
        public void AddPlObjEvent()
        {
            InfBar.PointMonitorTooltips.StartMonitor();

        }


        [CommandMethod("IBA")]
        async public void InsertBlockAsync()
        {
            var doc =
              app.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            //PromptSelectionOptions
            //ed.GetSelection();

            //Store the handle of the parent
            var parentHandle = "";

            // Let's ask the user to select the insertion point
            await ed.CommandAsync("_.INSERT", "N-PILLAR", Editor.PauseToken, 1, 1, 0);

            ed.WriteMessage("\nWe have inserted our block.");

            // Get the current document and database, and start a transaction
            Document acDoc = app.DocumentManager.MdiActiveDocument;
            Database acCurDb = acDoc.Database;

            using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
            {
                // Open the Block table record for read
                BlockTable acBlkTbl;
                acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                             OpenMode.ForRead) as BlockTable;

                // Open the Block table record Model space for write
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                                OpenMode.ForWrite) as BlockTableRecord;

                //Look through the Block table record
                foreach (ObjectId recordID in acBlkTblRec)
                {
                    //If the block is an inserted block then true
                    if (recordID.ObjectClass.DxfName == "INSERT")
                    {

                        BlockReference blkref = (BlockReference)acTrans.GetObject(recordID, OpenMode.ForRead);

                        AttributeCollection attributes = blkref.AttributeCollection;

                        //var ObjHandle = blkref.Handle.Value;
                        //int PilNo = 1;

                        //Store data for later modules
                        //Data.DataTable.storeData(ObjHandle, PilNo);

                        //Look for if the block has the attribute Parent
                        foreach (ObjectId attId in attributes)
                        {
                            AttributeReference attref = (AttributeReference)acTrans.GetObject(attId, OpenMode.ForRead);

                            if (attref.Tag == "Parent")
                            {
                                if (attref.TextString == "Unassigned")
                                {
                                    //attref.TextString = Parent;
                                }
                            }
                            //MessageBox.Show(recordID.ObjectClass.DxfName);
                            //break;
                        }
                    }
                    
                }
                acTrans.Commit();
            }
        }
    }
}
0 Likes
Accepted solutions (1)
1,187 Views
2 Replies
Replies (2)
Message 2 of 3

_gile
Consultant
Consultant
Accepted solution

Hi,

 

While you're scripting the INSERT command, you can keep on this way to specify the attribute value. You just have to play with the ATTREQ and ATTDIA system variable values.

 

Assuming "Parent" is the only one attribute.

        [CommandMethod("IBA1")]
        public async static void InsertBlockAsync1()
        {
            var ed = app.DocumentManager.MdiActiveDocument.Editor;
            var attreq = app.GetSystemVariable("ATTREQ");
            var attdia = app.GetSystemVariable("ATTDIA");
            try
            {
                app.SetSystemVariable("ATTREQ", 1);
                app.SetSystemVariable("ATTDIA", 0);
                await ed.CommandAsync("_insert", "N-PILLAR", Editor.PauseToken, 1.0, 1.0, 0.0, Parent);
            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex) when (ex.ErrorStatus == ErrorStatus.UserBreak) { }
            catch (System.Exception ex) { ed.WriteMessage($"\nError: {ex.Message}"); }
            finally
            {
                app.SetSystemVariable("ATTREQ", attreq);
                app.SetSystemVariable("ATTDIA", attdia);
            }
        }

Else, you can also get the ObjectId of the last created entity with Autodesk.AutoCAD.Internal.Entlast() method. As previously, you have to take care of the ATTREQ sysvar value.

        [CommandMethod("IBA2")]
        public async static void InsertBlockAsync2()
        {
            var doc = app.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            var attreq = app.GetSystemVariable("ATTREQ");
            try
            {
                app.SetSystemVariable("ATTREQ", 0);
                await ed.CommandAsync("_insert", "N-PILLAR", Editor.PauseToken, 1.0, 1.0, 0.0);
                var lastEntId = Autodesk.AutoCAD.Internal.Utils.EntLast();
                using (var tr = db.TransactionManager.StartTransaction())
                {
                    var br = (BlockReference)tr.GetObject(lastEntId, OpenMode.ForRead);
                    foreach (ObjectId id in br.AttributeCollection)
                    {
                        var attRef = (AttributeReference)tr.GetObject(id, OpenMode.ForRead);
                        if (attRef.Tag == "Parent")
                        {
                            tr.GetObject(id, OpenMode.ForWrite);
                            attRef.TextString = Parent;
                        }
                    }
                    tr.Commit();
                }
            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex) when (ex.ErrorStatus == ErrorStatus.UserBreak) { }
            catch (System.Exception ex) { ed.WriteMessage($"\nError: {ex.Message}"); }
            finally { app.SetSystemVariable("ATTREQ", attreq); }
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 3 of 3

_gile
Consultant
Consultant

But, using .NET to script native commands makes me feel like hunting mosquitoes with a bazooka.

You can insert a block reference by code, assign the attribute values and use a Jig if you want the user see the block during insertion.

 

Here're some re-usable extension methods to insert a block possibly with attributes.

using System;
using System.Collections.Generic;
using System.IO;

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace InsertBlockWithAttributes
{
    static class Extension
    {
        /// <summary>
        /// Gets a block definition ObjetcId by its name.
        /// If the block table does not contains the block a dwg file with the same name is searched in the search paths.
        /// </summary>
        /// <param name="blockTable">BlockTable instance this method applies to.</param>
        /// <param name="blockName">Block name.</param>
        /// <returns>The ObjectId of the block definition or ObjectId.Null if not found</returns>
        public static ObjectId GetBlock(this BlockTable blockTable, string blockName)
        {
            // check the arguments validity
            if (blockTable == null)
                throw new ArgumentNullException("blockTable");
            if (blockName == null)
                throw new ArgumentNullException("blockName");

            var db = blockTable.Database;
            // if the block table already contains the block, return the block ObjectId
            if (blockTable.Has(blockName))
                return blockTable[blockName];

            // search for a dwg file named blockName in the search path
            try
            {
                string ext = Path.GetExtension(blockName);
                if (ext == "")
                    blockName += ".dwg";
                string blockPath;
                if (File.Exists(blockName))
                    blockPath = blockName;
                else
                    blockPath = HostApplicationServices.Current.FindFile(blockName, db, FindFileHint.Default);

                // import the file in the block table and return its ObjectId
                blockTable.UpgradeOpen();
                using (var tmpDb = new Database(false, true))
                {
                    tmpDb.ReadDwgFile(blockPath, FileShare.Read, true, null);
                    return blockTable.Database.Insert(Path.GetFileNameWithoutExtension(blockName), tmpDb, true);
                }
            }
            // if the file is not found, return ObjectId.Null
            catch
            {
                return ObjectId.Null;
            }
        }

        /// <summary>
        /// Adds the attribute references to a block referenc
        /// </summary>
        /// <param name="target">BlockRefence instance this method applies to.</param>
        /// <param name="attValues">Values of the attributes by tag.</param>
        public static void AddAttributeReferences(this BlockReference target, Dictionary<string, string> attValues)
        {
            // check the arguments validity
            if (target == null)
                throw new ArgumentNullException("target");
            if (attValues == null)
                throw new ArgumentNullException("attValues");
            var db = target.Database;
            var tr = db.TransactionManager.TopTransaction;
            if (tr == null)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);

            // add the attribute references and set their values
            var btr = (BlockTableRecord)tr.GetObject(target.BlockTableRecord, OpenMode.ForRead);
            foreach (ObjectId id in btr)
            {
                if (id.ObjectClass.DxfName == "ATTDEF")
                {
                    var attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
                    if (!attDef.Constant)
                    {
                        var attRef = new AttributeReference();
                        attRef.SetAttributeFromBlock(attDef, target.BlockTransform);
                        if (attValues.ContainsKey(attDef.Tag))
                        {
                            attRef.TextString = attValues[attDef.Tag];
                        }
                        target.AttributeCollection.AppendAttribute(attRef);
                        tr.AddNewlyCreatedDBObject(attRef, true);
                    }
                }
            }
        }

        /// <summary>
        /// Insert a block reference in the specified BlockTableRecord
        /// </summary>
        /// <param name="owner">BlockTableRecord instance this method applies to.</param>
        /// <param name="blockName">Name of the block to insert</param>
        /// <param name="position">Poisition of the block reference</param>
        /// <param name="attValues">Values of the attributes by tag if not null.</param>
        /// <returns></returns>
        public static BlockReference InsertBlockReference(this BlockTableRecord owner, string blockName, Point3d position, Dictionary<string, string> attValues = null)
        {
            // check the arguments validity
            if (owner == null)
                throw new ArgumentNullException("owner");
            if (blockName == null)
                throw new ArgumentNullException("blockName");
            var db = owner.Database;
            var tr = db.TransactionManager.TopTransaction;
            if (tr == null)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
            var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
            var btrId = bt.GetBlock(blockName);
            if (btrId.IsNull)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidBlockName);

            // create a new block reference
            var br = new BlockReference(position, btrId);
            owner.AppendEntity(br);
            tr.AddNewlyCreatedDBObject(br, true);

            // create the attribute references
            if (attValues != null)
                br.AddAttributeReferences(attValues);
            return br;
        }
    }
}

Here're some classes derived from EntityJig to display a block (possibly attributed) during insertion.

using System;
using System.Collections.Generic;

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;

namespace InsertBlockWithAttributes
{
    /// <summary>
    /// Provides methods to display a block reference during insertion
    /// </summary>
    class BlockJig : EntityJig
    {
        protected Point3d position;
        protected BlockReference br;

        public BlockJig(BlockReference br)
            : base(br)
        {
            this.br = br ?? throw new ArgumentNullException("br");
            position = br.Position;
        }

        protected override SamplerStatus Sampler(JigPrompts prompts)
        {
            string msg = "\nSpecify insertion point: ";
            var jppo = new JigPromptPointOptions(msg);
            jppo.UserInputControls = UserInputControls.Accept3dCoordinates;
            PromptPointResult ppr = prompts.AcquirePoint(jppo);
            if (position.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint)
            {
                return SamplerStatus.NoChange;
            }
            position = ppr.Value;
            return SamplerStatus.OK;
        }

        protected override bool Update()
        {
            br.Position = position;
            return true;
        }
    }

    /// <summary>
    /// Provides methods to display a block reference and its attributes during insertion
    /// </summary>
    class BlockAttribJig : BlockJig
    {
        private Dictionary<string, TextInfo> attInfos;

        public BlockAttribJig(BlockReference br)
            : base(br)
        {
            attInfos = new Dictionary<string, TextInfo>();
            var btr = (BlockTableRecord)br.BlockTableRecord.GetObject(OpenMode.ForRead);
            foreach (ObjectId id in btr)
            {
                if (id.ObjectClass.DxfName == "ATTDEF")
                {
                    var attDef = (AttributeDefinition)id.GetObject(OpenMode.ForRead);
                    if (!attDef.Constant)
                    {
                        var textInfo = new TextInfo(
                            attDef.Position, attDef.AlignmentPoint, attDef.Justify != AttachmentPoint.BaseLeft);
                        attInfos.Add(attDef.Tag, textInfo);
                    }
                }
            }
        }

        protected override bool Update()
        {
            base.Update();
            foreach (ObjectId id in br.AttributeCollection)
            {
                var att = (AttributeReference)id.GetObject(OpenMode.ForWrite);
                string tag = att.Tag;
                if (attInfos.ContainsKey(tag))
                {
                    TextInfo ti = attInfos[tag];
                    att.Position = ti.Position.TransformBy(br.BlockTransform);
                    if (ti.IsAligned)
                    {
                        att.AlignmentPoint =
                            ti.Alignment.TransformBy(br.BlockTransform);
                        att.AdjustAlignment(br.Database);
                    }
                    if (att.IsMTextAttribute)
                    {
                        att.UpdateMTextAttribute();
                    }
                }
            }
            return true;
        }

        struct TextInfo
        {
            public Point3d Position { get; private set; }

            public Point3d Alignment { get; private set; }

            public bool IsAligned { get; private set; }

            public TextInfo(Point3d position, Point3d alignment, bool aligned)
            {
                Position = position;
                Alignment = alignment;
                IsAligned = aligned;
            }
        }
    }
}

A command example:

        [CommandMethod("IBC")]
        public static void InsertBlockByCode()
        {
            var doc = app.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                // intert block and attribute at origin
                var insertPoint = Point3d.Origin;
                var attValues = new Dictionary<string, string> { ["Parent"] = Parent };
                var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                var br = curSpace.InsertBlockReference("N-PILLAR", insertPoint, attValues);

                // drag the block and attribute (erase it if user cancles)
                var jig = new BlockAttribJig(br);
                var result = ed.Drag(jig);
                if (result.Status != PromptStatus.OK)
                {
                    br.Erase();
                }
                tr.Commit();
            }
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes