Author Topic: Copy block into another  (Read 1598 times)

0 Members and 1 Guest are viewing this topic.

latour_g

  • Newt
  • Posts: 184
Copy block into another
« on: June 07, 2013, 09:09:46 AM »
Hi,
I have a function that copy a block into another.  First, you select the block to be replace and after you select the replacement block.
It works fine except for one thing : the first time copy works fine but if I choose to take the block that has just been replace, it's going to say that the attribute has been erase (I know that in my code I do erase the attribute but if I don't I will keep the ''old'' attrbiutes).  If I close the drawing and open it again, I will be able to do the copy without having this message so it's probably not much that I forgot to do but I don't see what. Thanks for any help !

Code: [Select]
       
private void btnCopyBlock_Click(object sender, EventArgs e)
        {
            Document doc = AcadApp.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
           
            TypedValue[] values = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") };
            SelectionFilter filter = new SelectionFilter(values);
            PromptSelectionOptions SelOpts = new PromptSelectionOptions();
            SelOpts.AllowDuplicates = false;
            SelOpts.MessageForAdding = MB.rmM.GetString("SelObj");
            SelOpts.MessageForRemoval = MB.rmM.GetString("RemObj");
            Utils.SetFocusToDwgView();
            PromptSelectionResult res = ed.GetSelection(SelOpts, filter);
            if (res.Status != PromptStatus.OK) return;

            PromptEntityOptions peo = new PromptEntityOptions(MB.rmM.GetString("SelBlock"));
            peo.SetRejectMessage(MB.rmM.GetString("SelNoBlock"));
            peo.AllowNone = false;
            peo.AddAllowedClass(typeof(BlockReference), false);
            PromptEntityResult per = ed.GetEntity(peo);

            if (per.Status != PromptStatus.OK) return;

            DocumentLock loc = doc.LockDocument();
            using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead);
                // replacement block
                BlockReference blkR = (BlockReference)tr.GetObject(per.ObjectId, OpenMode.ForRead);
                BlockTableRecord btNr = (BlockTableRecord)tr.GetObject(bt[blkR.Name], OpenMode.ForRead);
                //
                SelectionSet ss = res.Value;
                ObjectId[] idArray = ss.GetObjectIds();

                foreach (ObjectId id in idArray)
                {
                    SortedList<string, string> attLst = new SortedList<string, string>();
                    BlockReference blk = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);

                    // attributes from the block to be replace
                    foreach (ObjectId attId in blk.AttributeCollection)
                    {
                        AttributeReference att = (AttributeReference)tr.GetObject(attId, OpenMode.ForWrite);
                        att.Erase(true);
                    }

                    // attributes from the replacement block
                    foreach (ObjectId attId in blkR.AttributeCollection)
                    {
                        AttributeReference att = (AttributeReference)tr.GetObject(attId, OpenMode.ForRead);
                        if (!attLst.ContainsKey(att.Tag)) attLst.Add(att.Tag, att.TextString);
                    }

                    blk.BlockTableRecord = btNr.ObjectId;

                    if (btNr.HasAttributeDefinitions)
                    {
                         IEnumerator Ie = btNr.GetEnumerator();
                        while (Ie.MoveNext())
                        {
                            try
                            {
                                DBObject obj = tr.GetObject((ObjectId)Ie.Current, OpenMode.ForRead, false);
                                if (obj.GetType() == typeof(AttributeDefinition))
                                {
                                    AttributeDefinition attDef = (AttributeDefinition)obj;
                                    AttributeReference attRef = new AttributeReference();
                                    attRef.SetDatabaseDefaults();
                                    attRef.Layer = attDef.Layer;
                                    attRef.SetAttributeFromBlock(attDef, blk.BlockTransform);
                                    attRef.Position = attDef.Position.TransformBy(blk.BlockTransform);
                                    attRef.Position = attDef.Position;
                                    if (attLst.ContainsKey(attDef.Tag)) attRef.TextString = attLst[attDef.Tag];
                                    //Add the attribute reference to the block ref 
                                    blk.AttributeCollection.AppendAttribute(attRef);
                                    tr.AddNewlyCreatedDBObject(attRef, true);
                                }
                            }
                            catch (System.Exception ex) { MessageBox.Show(ex.ToString()); }
                        }
                    }
                }
                tr.Commit();
            }
            loc.Dispose();
            Utils.PostCommandPrompt();
        }

TheMaster

  • Guest
Re: Copy block into another
« Reply #1 on: June 07, 2013, 11:10:17 AM »
The AttributeCollection is most-likely returning erased attributes, and is what would cause the error you're getting. You should be checking to see if each AttributeReference's ObjectId is erased before trying to open it.

Just looking at your code, there's a few other issues there, although they may not be causing errors.

Code - C#: [Select]
  1. BlockTableRecord btNr = (BlockTableRecord)tr.GetObject(bt[blkR.Name], OpenMode.ForRead);
  2.  

The ObjectId of the referenced BlockTableRecord is the value of the BlockReference's DynamicBlockTableRecord property (for dynamic blocks, this is the definition block rather than the anonymous block), so you don't have to look up the BlockTableRecord's ObjectId using the block table and the block's name, you just use the aforementioned property.

A few other issues include your doing a lot of work for each BlockReference, that you could only do once (matching up attributes, since it will always be the same).

Your code also uses GetEnumerator()/MoveNext() which it shouldn't be doing (perhaps you've been reading Jerry Winter's books?). The foreach() loop should be used instead.  You also don't ensure that objects that must be disposed are disposed in the event your code fails (the document lock, for example).

Lastly, attempting to do a replacement involving dynamic blocks (either or both), is probably going to fail, because they use anonymous blocks for variations, and have a lot of additional data attached to them that is dependent on the block definition.

latour_g

  • Newt
  • Posts: 184
Re: Copy block into another
« Reply #2 on: June 07, 2013, 01:13:20 PM »
Thank you, I added if (attId.IsErased) continue; in my code and now it's working fine !
I will check for the other point you mentionned.  As for Jerry Winter's books, I don't know because this function was written by someone else than me.
Thanks again !

Code: [Select]
foreach (ObjectId attId in blk.AttributeCollection)
{
       if (attId.IsErased) continue;
       AttributeReference att = (AttributeReference)tr.GetObject(attId, OpenMode.ForWrite);
       att.Erase(true);
}