using System; using System.Collections; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using AS = Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; namespace XRefDrawOrder { public class CommandLaunchers { [CommandMethod("XRDO")] public static void XRefManage() { ArrangeXRefs xrM = new ArrangeXRefs(); xrM.Show(); } } public class ArrangeXRefs : Form { private TreeView XRefListTView; //private CheckBox SuppressMessages; private Button UpdateBtn, CloseBtn, HelpBtn; TreeNode DragDropSourceNode, DragDropTargetNode; AS.Document doc; Database db; Editor ed; int ControlGap = 10; Color NodeHighlightColour = Color.Orange; public ArrangeXRefs() { InitialiseComponents(); } void InitialiseComponents() { //Instructions = new RichTextBox(); //Instructions.Location = new Point(5, 5); //Instructions.Size = new System.Drawing.Size(470, 120); XRefListTView = new TreeView(); XRefListTView.Location = new Point(5, 5); XRefListTView.Size = new Size(300, 350); XRefListTView.AllowDrop = true; XRefListTView.AfterSelect += XRefList_AfterSelect; XRefListTView.BeforeSelect += XRefList_BeforeSelect; XRefListTView.ItemDrag += XRefList_ItemDrag; XRefListTView.DragEnter += XRefList_DragEnter; XRefListTView.DragDrop += XRefList_DragDrop; HelpBtn = new Button(); HelpBtn.Text = "Help"; HelpBtn.Width = Utilities.GetControlWidth(HelpBtn, 10); HelpBtn.Location = new Point(XRefListTView.Right - HelpBtn.Width, XRefListTView.Bottom + 5); HelpBtn.Click += Help_Click; CloseBtn = new Button(); CloseBtn.Text = "Close"; CloseBtn.Width = Utilities.GetControlWidth(CloseBtn, 10); CloseBtn.Location = new Point(HelpBtn.Left - ControlGap - CloseBtn.Width, XRefListTView.Bottom + 5); CloseBtn.Click += Close_Click; UpdateBtn = new Button(); UpdateBtn.Text = "Update"; UpdateBtn.Width = Utilities.GetControlWidth(UpdateBtn, 10); UpdateBtn.Location = new Point(CloseBtn.Left - ControlGap - UpdateBtn.Width, XRefListTView.Bottom + 5); UpdateBtn.Click += Update_Click; this.ClientSize = new Size(XRefListTView.Right + 5, CloseBtn.Bottom + 5); this.Load += ArrangeXRefs_Load; this.Controls.Add(XRefListTView); this.Controls.Add(HelpBtn); this.Controls.Add(CloseBtn); this.Controls.Add(UpdateBtn); this.TopMost = true; } void Help_Click(object sender, EventArgs e) { Instructions i = new Instructions(); i.ShowDialog(); } // Events private void ArrangeXRefs_Load(object sender, EventArgs e) { doc = doc = AS.Application.DocumentManager.MdiActiveDocument; db = doc.Database; ed = doc.Editor; List xrefEnts = Utilities.GetXRefEntities(db, ed); if(xrefEnts.Count > 0) Utilities.ProcessXRefsToTreeView(db, XRefListTView, xrefEnts); else { MessageBox.Show("No XRefs to manage."); this.Close(); } } private void XRefList_BeforeSelect(object sender, TreeViewCancelEventArgs e) { Utilities.ResetTreeViewBackColour(XRefListTView); } private void XRefList_AfterSelect(object sender, TreeViewEventArgs e) { XRefListTView.SelectedNode.BackColor = NodeHighlightColour; } private void Close_Click(object sender, EventArgs e) { this.Close(); } private void Update_Click(object sender, EventArgs e) { AS.DocumentLock docLock = doc.LockDocument(); using (Transaction t = db.TransactionManager.StartTransaction()) { DrawOrderTable dot = Utilities.GetModelSpaceDrawOrderTable(db); List ents = new List(); foreach (TreeNode node in XRefListTView.Nodes) ents.Add((Entity)node.Tag); for (int ei = 0; ei < ents.Count; ei++) { Entity ent = ents[ei]; ObjectIdCollection ids = new ObjectIdCollection(); ids.Add(ent.ObjectId); if (ei == 0) dot.MoveToTop(ids); else dot.MoveBelow(ids, ents[ei - 1].ObjectId); } t.Commit(); } Utilities.SendACommand("Regenall "); } // Drag 'n' Drop private void XRefList_ItemDrag(object sender, ItemDragEventArgs e) { DragDropSourceNode = e.Item as TreeNode; DoDragDrop(e.Item, DragDropEffects.Move); } private void XRefList_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } private void XRefList_DragDrop(object sender, DragEventArgs e) { Point pos = XRefListTView.PointToClient(new Point(e.X, e.Y)); DragDropTargetNode = XRefListTView.GetNodeAt(pos); TreeNode nodeCopy = new TreeNode(); if (DragDropTargetNode != null) { //if (!SuppressMessages.Checked) //{ // MessageBoxButtons mbb = MessageBoxButtons.YesNo; // string message = string.Format("About to drag drawing '{0}' above drawing '{1}'\n\nIs this OK?", DragDropSourceNode.Name, DragDropTargetNode.Name); // if (MessageBox.Show(message, "Alert", mbb) == System.Windows.Forms.DialogResult.Yes) // DragNDropnode(nodeCopy); //} //else DragNDropnode(nodeCopy); } else { //if (!SuppressMessages.Checked) //{ // MessageBoxButtons mbb = MessageBoxButtons.YesNo; // string message = string.Format("Drawing '{0}' will be placed at the bottom of the list.\n\nIs this OK?", DragDropSourceNode.Name); // if (MessageBox.Show(message, "Alert", mbb) == System.Windows.Forms.DialogResult.Yes) // DragNDropnode(nodeCopy, true); //} //else DragNDropnode(nodeCopy, true); } XRefListTView.Invalidate(); XRefListTView.SelectedNode = nodeCopy; XRefListTView.Focus(); } void DragNDropnode(TreeNode NodeCopy, bool ToBottom = false) { NodeCopy = (TreeNode)DragDropSourceNode.Clone(); if (!ToBottom) XRefListTView.Nodes.Insert(XRefListTView.Nodes.IndexOf(DragDropTargetNode), NodeCopy); else XRefListTView.Nodes.Add(NodeCopy); DragDropSourceNode.Remove(); } } public class Instructions : Form { private RichTextBox InstructionsRTF; public Instructions() { Initialise(); } void Initialise() { InstructionsRTF = new RichTextBox(); InstructionsRTF.Location = new Point(5, 5); InstructionsRTF.Size = new System.Drawing.Size(470, 150); this.ClientSize = new Size(InstructionsRTF.Right + 5, InstructionsRTF.Bottom + 5); this.Controls.Add(InstructionsRTF); this.Load += Instructions_Load; } void Instructions_Load(object sender, EventArgs e) { string msg = @"{\rtf1\ansi\ansicpg1252\deff0\deflang3081{\fonttbl{\f0\fswiss\fprq2\fcharset0 Microsoft Sans Serif;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fnil\fcharset0 Calibri;}}" + @"{\*\generator Msftedit 5.41.21.2510;}\viewkind4\uc1\pard\ri-20\b\f0\fs24 Instructions\b0\par" + @"\fs17 Below is a list of XRefs in this drawing.\par" + @"\par" + @" You can drag and drop each XRef to re-order them. \par" + @"\pard\ri-20\tx284\f1\'b7\tab\f0 The dragged XRef will be placed above the item you drop it onto.\par" + @"\f1\'b7\tab\f0 If you drag an XRef past the end of the list, it will be placed at the bottom of the list.\par" + @"\pard\ri-20\par" + @"\pard\ri-20\sa200\sl276\slmult1 Click Update to commit the reordered list to the drawing.\lang9\f2\fs22\par" + @"}"; Utilities.SetRTFMessage(InstructionsRTF, msg); } } public class Utilities { public static int GetControlWidth(Control SourceControl, int Margin) { return ((int)SourceControl.CreateGraphics().MeasureString(SourceControl.Text, SourceControl.Font).Width + Margin); } public static List GetXRefEntities(Database CurrDBase, Editor CurrED) { // Initialise our return List result = new List(); // Build a selection filter looking for 'INSERT' objects (includes blocks and xrefs) from modelspace (DXF code 67 = 0) TypedValue[] tvs = { new TypedValue(0, "INSERT"), new TypedValue(67, 0) }; SelectionFilter sf = new SelectionFilter(tvs); // Get objects matching the selection filter. PromptSelectionResult psr = CurrED.SelectAll(sf); // Establish a descending integer comparer. See class in full code. IComparer comparer = new DescendingIntComparer(); /* Apply it to a SortedList storing DrawOrderTable index and entity. The order the entities are captured in the PromptSelectResult, may not be the order they appear in the DrawOrderTable. However the indexes of the objectIds in the DrawOrderTable are such that highest index is on top. The SortedList, with the DescendingIntComparer applied, automatically sorts highest DrawOrderTable index to lowest This ensures that the entities are returned in the correct order.*/ SortedList entsList = new SortedList(comparer); // As long as there is something selected. if (psr.Status == PromptStatus.OK) { // Get the object Ids from the selection. ObjectId[] ids = psr.Value.GetObjectIds(); using (Transaction t = CurrDBase.TransactionManager.StartTransaction()) { // Get the modelspace draw order table. DrawOrderTable dot = Utilities.GetModelSpaceDrawOrderTable(CurrDBase); // Get the ObjectIds of all the entities in the draw order table. byte honor = 0; ObjectIdCollection fdoIDs = dot.GetFullDrawOrder(honor); foreach (ObjectId id in ids) { // Get the block reference of the ID. BlockReference bref = t.GetObject(id, OpenMode.ForRead, false, true) as BlockReference; // Get the block table record for the block reference. BlockTableRecord btr = bref.BlockTableRecord.GetObject(OpenMode.ForRead) as BlockTableRecord; // If the record is an external reference if (btr.IsFromExternalReference) { // Get the entity for the record. Entity ent = t.GetObject(id, OpenMode.ForRead) as Entity; // Get the index of the entity in the draw order table int entIndex = Utilities.GetDrawTableOrderOfEntity(fdoIDs, ent); // Add the entity and its draw order index to the SortedList entsList.Add(entIndex, ent); } } t.Commit(); } } // For each of the entities in the SortedList foreach (int key in entsList.Keys) // Add the the returned entity list. result.Add(entsList[key]); return result; } public static void ProcessXRefsToTreeView(Database CurrDBase, TreeView TargetTreeView, List XRefsEnts) { using (Transaction t = CurrDBase.TransactionManager.StartTransaction()) { foreach (Entity e in XRefsEnts) { BlockReference bref = t.GetObject(e.ObjectId, OpenMode.ForRead) as BlockReference; TreeNode node = new TreeNode(); node.Name = node.Text = bref.Name; node.Tag = e; TargetTreeView.Nodes.Add(node); } t.Commit(); } } public static void ResetTreeViewBackColour(TreeView TargetTView) { if (TargetTView.Nodes.Count > 0) foreach (TreeNode node in TargetTView.Nodes) RecurseTreeViewNodesColour(node, TargetTView.BackColor); } static void RecurseTreeViewNodesColour(TreeNode StartNode, Color NodeColour) { StartNode.BackColor = NodeColour; if (StartNode.Nodes.Count > 0) foreach (TreeNode node in StartNode.Nodes) RecurseTreeViewNodesColour(node, NodeColour); } public static void SendACommand(string CommandText) { Object acObj = AS.Application.AcadApplication; object acDoc = acObj.GetType().InvokeMember("ActiveDocument", System.Reflection.BindingFlags.GetProperty, null, acObj, null); acDoc.GetType().InvokeMember("SendCommand", System.Reflection.BindingFlags.InvokeMethod, null, acDoc, new object[] { CommandText }); } public static void SetRTFMessage(RichTextBox Messages, string Message) { Messages.Invoke((Action)(() => Messages.Rtf = Message)); } public static DrawOrderTable GetModelSpaceDrawOrderTable(Database CurrDBase) { DrawOrderTable dot = null; using (Transaction t = CurrDBase.TransactionManager.StartTransaction()) { BlockTable bt = t.GetObject(CurrDBase.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = t.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord; dot = t.GetObject(btr.DrawOrderTableId, OpenMode.ForWrite) as DrawOrderTable; t.Commit(); } return dot; } public static int GetDrawTableOrderOfEntity(ObjectIdCollection FullDrawOrder, Entity TestEnt) { int fdoCnt = FullDrawOrder.Count; ObjectId entId = TestEnt.Id; for(int i= 0; i < fdoCnt; i++) { ObjectId fdoId = FullDrawOrder[i]; if (fdoId == entId) return i; } return -1; } } internal class DescendingIntComparer : IComparer { public int Compare(int x, int y) { try { return System.Convert.ToInt32(x).CompareTo (System.Convert.ToInt32(y)) * -1; } catch (System.Exception ex) { return x.ToString().CompareTo(y.ToString()); } } } }