000

Draw Order Problem with Dynamic Block

.
In one of my recent AutoCAD programming project, the code has to deal with a quite complicated dynamic block, which has many visibility states and a few parameters associated with stretching/moving actions. The entities in the block definition include texts, attributes, various geometric entities and hatch. The hatch in this dynamic block is used as background of other entities (text, attribute). Naturally, when defining the block, the Draw Order of the hatches is set to be at bottom.

When this dynamic block is used, occasionally, users report that the Draw Order of the hatch in inserted block reference is wrong, e.g. the hatch somehow is on top other entities, thus visually blocks other entities.

On the first a few reports, I thought it must the draw order in the block definition wasn't set correct, because the block is so complicated and has so many entities in it. However, after carefully examined the block and reset the draw orders of the entities in the block definition again and again, users still report this issue from time to time. This prompted me to think that it must have something to do with the fact that the block is a dynamic block.

This is my speculation on this issue:

When a dynamic block is inserted into drawing and one of its dynamic property is set to a value different from the value when the block is defined, the block reference is no longer a reference to the original block definition. AutoCAD dynamically defines a new block definition as an anonymous block, based on the original block definition and the dynamic property value. That means, the entities in the newly defined anonymous block in created dynamically at the time when this anonymous block definition is needed. So, it is possible, the entities are created in a order that is different from the Draw Order saved in the original block definition's DrawOrderTable, thus the issue. Again, this is just my guess. Only those in Autodesk who actually built dynamic block mechanism would knew whether my guess is close to the fact or not.

Regardless my guess being right or wrong, the issue is a real pain when is occurs. It can be corrected by using Block Editor in the drawing where the block reference presents. But in my case, it is not an easy thing to do, as I stated, the block is very complicated and has a lot entities overlapped together. So, I had to write a bit of code so that user can execute a single command to fix this issue easily. Following is the sample code to set "Draw Order" in a block (definition). The code used in my real project is a bit different, so that all targeting blocks are fixed at once.

    1 using Autodesk.AutoCAD.ApplicationServices;
    2 using Autodesk.AutoCAD.DatabaseServices;
    3 using Autodesk.AutoCAD.EditorInput;
    4 using Autodesk.AutoCAD.Runtime;
    5 
    6 [assembly: CommandClass(typeof(DrawOrderInBlock.MyCommands))]
    7 
    8 namespace DrawOrderInBlock
    9 {
   10     public class MyCommands
   11     {
   12         [CommandMethod("DOinBlock")]
   13         public static void RunMyCommand()
   14         {
   15             Document dwg = Application.DocumentManager.MdiActiveDocument;
   16             Editor ed = dwg.Editor;
   17 
   18             //Ask user to select a block. In real world, I use code
   19             //to select all targeting blocks by its name, so that
   20             //user does not have to select block manually
   21             PromptEntityOptions opt = new PromptEntityOptions(
   22                 "nSelect a block:");
   23             opt.SetRejectMessage("\nInvalid: not a block.");
   24             opt.AddAllowedClass(typeof(BlockReference), true);
   25             PromptEntityResult res = ed.GetEntity(opt);
   26             if (res.Status == PromptStatus.OK)
   27             {
   28                 SetDrawOrderInBlock(dwg, res.ObjectId);
   29                 ed.Regen();
   30             }
   31             else
   32             {
   33                 ed.WriteMessage("\n*Cancel*");
   34             }
   35 
   36             Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
   37         }
   38 
   39         private static void SetDrawOrderInBlock(Document dwg, ObjectId blkId)
   40         {
   41             using (Transaction tran =
   42                 dwg.TransactionManager.StartTransaction())
   43             {
   44                 BlockReference bref = (BlockReference)tran.GetObject(
   45                     blkId, OpenMode.ForRead);
   46 
   47                 BlockTableRecord bdef = (BlockTableRecord)tran.GetObject(
   48                     bref.BlockTableRecord, OpenMode.ForWrite);
   49 
   50                 DrawOrderTable doTbl = (DrawOrderTable)tran.GetObject(
   51                     bdef.DrawOrderTableId, OpenMode.ForWrite);
   52 
   53                 ObjectIdCollection col = new ObjectIdCollection();
   54                 foreach (ObjectId id in bdef)
   55                 {
   56                     if (id.ObjectClass.DxfName.ToUpper() == "HATCH")
   57                     {
   58                         col.Add(id);
   59                     }
   60                 }
   61 
   62                 if (col.Count > 0)
   63                 {
   64                     doTbl.MoveToBottom(col);
   65                 }
   66 
   67                 tran.Commit();
   68             }
   69         }
   70     }
   71 }
 
This video clip shows how the code work: a hatch in the block is set in front of crossed 2 lines; after executing the code, the hatch is set to bottom in Draw Order table, so that the crossed lines become visible in front of the hatch.
 

Blog Archive