Populating a tree view inside AutoCAD with sheet set data using .NET - Part 2

I didn't realise when I created the last post (with code borrowed from Fenton) that this would become a multi-part series – otherwise I'd clearly have called the earlier post "Part 1". 🙂

A comment from Harold Comerro requested information on getting more from the DST than was previously shown. Today's post extends the previous code to create two different slices of the data: a "Sheets View" and a "Database View", both hosted in the same palette set.

Here's the updated C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Windows;

using acApp =

  Autodesk.AutoCAD.ApplicationServices;

using ACSMCOMPONENTS18Lib;

using System.Windows.Forms;

using System;

 

namespace MyApplication

{

  public class Commands

  {

    static PaletteSet ps = null;

    static UserControl1 sheetsControl = null;

    static UserControl1 dbControl = null;

 

    [CommandMethod("SSTREE")]

    public void PopulateCustomSheetTree()

    {

      // Check the state of the paletteset

 

      if (ps == null)

      {

        // Then create it

 

        ps = new PaletteSet("Custom Sheet Tree");

 

        // Create and add our "sheets" view

 

        sheetsControl = new UserControl1();

        ps.Add("Sheets View", sheetsControl);

        sheetsControl.treeView1.ShowNodeToolTips = true;

 

        // Create and add our "database" view

 

        dbControl = new UserControl1();

        ps.Add("Database View", dbControl);

        dbControl.treeView1.ShowNodeToolTips = true;

      }

 

      ps.Visible = true;

 

      // Get the AutoCAD Editor

 

      Editor ed =

        acApp.Application.DocumentManager.MdiActiveDocument.Editor;

 

      // Get the SheetSet Manager

 

      AcSmSheetSetMgr mgr = new AcSmSheetSetMgr();

 

      // Create a new SheetSet Database

 

      AcSmDatabase db = new AcSmDatabase();

 

      // Try and load a default DST file...

 

      try

      {

        db =

          mgr.OpenDatabase(

            @"C:\Program Files\Autodesk\AutoCAD 2011\Sample\" +

            @"Sheet Sets\Architectural\IRD Addition.dst",

            true

          );

      }

      catch (System.Exception ex)

      {

        ed.WriteMessage(ex.ToString());

        return;

      }

 

      // Lock the db for processing

 

      db.LockDb(db);

 

      // Create our root item in the "database" tree view

 

      TreeNode dbRoot = new TreeNode(db.GetFileName());

      dbControl.treeView1.Nodes.Add(dbRoot);

 

      // Process the items owned by the database

 

      try

      {

        ProcessItems(db, false, dbRoot);

      }

      catch { }

 

      // Create our root item in the "sheets" tree view

 

      AcSmSheetSet ss = db.GetSheetSet();

      TreeNode sheetsRoot = new TreeNode(ss.GetName());

      sheetsControl.treeView1.Nodes.Add(sheetsRoot);

 

      // Use the sheet enumerator to process the contents

 

      try

      {

        ProcessEnumerator(ss.GetSheetEnumerator(), true, sheetsRoot);

      }

      catch { }

 

      db.UnlockDb(db, true);

      mgr.Close(db);

 

      // Expand our trees

 

      sheetsControl.treeView1.ExpandAll();

      dbControl.treeView1.ExpandAll();

    }

 

    // A number of functions to take advantage of different

    // AcSm enumerator interfaces

 

    void ProcessEnumerator(

      IAcSmEnumComponent iter, bool useEnum, TreeNode root

    )

    {

      IAcSmComponent item = iter.Next();

      while (item != null)

      {

        ProcessItem(item, useEnum, root);

 

        item = iter.Next();

      }

    }

 

    void ProcessEnumerator(

      IAcSmEnumPersist iter, bool useEnum, TreeNode root

    )

    {

      IAcSmPersist pers = iter.Next();

      IAcSmComponent item = pers as IAcSmComponent;

      while (pers != null)

      {

        if (item != null)

          ProcessItem(item, useEnum, root);

 

        pers = iter.Next();

        item = pers as IAcSmComponent;

      }

    }

 

    void ProcessEnumerator(

      IAcSmEnumSheetSelSet iter, bool useEnum, TreeNode root

    )

    {

      IAcSmSheetSelSet selset = iter.Next();

      IAcSmComponent item = selset as IAcSmComponent;

      while (selset != null)

      {

        if (item != null)

          ProcessItem(item, useEnum, root);

 

        selset = iter.Next();

        item = selset as IAcSmComponent;

      }

    }

 

    void ProcessEnumerator(

      IAcSmEnumSheetView iter, bool useEnum, TreeNode root

    )

    {

      IAcSmSheetView shv = iter.Next();

      IAcSmComponent item = shv as IAcSmComponent;

      while (shv != null)

      {

        if (item != null)

          ProcessItem(item, useEnum, root);

 

        shv = iter.Next();

        item = shv as IAcSmComponent;

      }

    }

 

    // A function to loop through and process a set of

    // items via their ownership hierarchy

 

    void ProcessItems(

      IAcSmPersist pers, bool useEnum, TreeNode root

    )

    {

      System.Array arr;

      pers.GetDirectlyOwnedObjects(out arr);

      if (arr != null)

      {

        foreach (object obj in arr)

        {

          IAcSmPersist item = obj as IAcSmPersist;

          if (item != null)

            ProcessItem(item, useEnum, root);

        }

      }

    }

 

    // Our main processing function which is called by

    // all the others, sooner or later

 

    void ProcessItem(

      IAcSmPersist item, bool useEnum, TreeNode root

    )

    {

      string type = item.GetTypeName();

      switch (type)

      {

        case "AcSmDatabase":

          {

            AcSmDatabase db = (AcSmDatabase)item;

 

            TreeNode tn = AddTreeNode(root, "Database", type);

            if (useEnum)

            {

              IAcSmEnumPersist enumerator =

                db.GetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(db, useEnum, tn);

            }

          }

          break;

 

        case "AcSmSheetSet":

          {

            AcSmSheetSet ss = (AcSmSheetSet)item;

 

            TreeNode tn = AddTreeNode(root, "Sheet set", type);

            if (useEnum)

            {

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)ss.GetSheetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(ss, useEnum, tn);

            }

          }

          break;

 

        case "AcSmSheetSelSets":

          {

            AcSmSheetSelSets selsets = (AcSmSheetSelSets)item;

 

            TreeNode tn =

              AddTreeNode(root, "Sheet selection sets", type);

            IAcSmEnumSheetSelSet enumerator =

              selsets.GetEnumerator();

            ProcessEnumerator(enumerator, useEnum, tn);

          }

          break;

 

        case "AcSmSubset":

          {

            AcSmSubset subset = (AcSmSubset)item;

            string subName = subset.GetName();

 

            if (!String.IsNullOrEmpty(subName))

            {

              TreeNode tn = AddTreeNode(root, subName, type);

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)subset.GetSheetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

          }

          break;

 

        case "AcSmSheet":

          {

            AcSmSheet sh = (AcSmSheet)item;

            string shName = sh.GetName();

 

            if (!String.IsNullOrEmpty(shName))

            {

              TreeNode tn = AddTreeNode(root, shName, type);

              if (useEnum)

              {

                AcSmSheetViews shvs = sh.GetSheetViews();

                ProcessItem(shvs, useEnum, tn);

              }

              else

              {

                ProcessItems(sh, useEnum, tn);

              }

            }

          }

          break;

 

        case "AcSmSheetViews":

          {

            AcSmSheetViews shvs = (AcSmSheetViews)item;

 

            TreeNode tn = AddTreeNode(root, "Sheet views", type);

            if (useEnum)

            {

              IAcSmEnumSheetView enumerator = shvs.GetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(shvs, useEnum, tn);

            }

          }

          break;

 

        case "AcSmSheetView":

          {

            AcSmSheetView sv = (AcSmSheetView)item;

            string svName = sv.GetName();

 

            if (!String.IsNullOrEmpty(svName))

              AddTreeNode(root, svName, type);

          }

          break;

 

        case "AcSmViewCategories":

          {

            AcSmViewCategories cats = (AcSmViewCategories)item;

 

            TreeNode tn =

              AddTreeNode(root, "View categories", type);

            if (useEnum)

            {

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)cats.GetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(cats, useEnum, tn);

            }

          }

          break;

 

        case "AcSmViewCategory":

          {

            AcSmViewCategory cat = (AcSmViewCategory)item;

            AddTreeNode(

              root,

              "View category: " + cat.GetName(),

              type

            );

          }

          break;

 

        case "AcSmCustomPropertyBag":

          {

            AcSmCustomPropertyBag bag =

              (AcSmCustomPropertyBag)item;

 

            TreeNode tn =

              AddTreeNode(root, "Custom property bag", type);

            if (useEnum)

            {

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)bag.GetPropertyEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(bag, useEnum, tn);

            }

          }

          break;

 

        case "AcSmCustomPropertyValue":

          {

            AcSmCustomPropertyValue pv =

              (AcSmCustomPropertyValue)item;

 

            AddTreeNode(

              root,

              "Custom property value: " + pv.GetValue().ToString(),

              type

            );

          }

          break;

 

        case "AcSmCalloutBlocks":

          {

            AcSmCalloutBlocks blks = (AcSmCalloutBlocks)item;

 

            TreeNode tn = AddTreeNode(root, "Callout blocks", type);

            if (useEnum)

            {

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)blks.GetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(blks, useEnum, tn);

            }

          }

          break;

 

        case "AcSmCalloutBlockReferences":

          {

            AcSmCalloutBlockReferences refs =

              (AcSmCalloutBlockReferences)item;

 

            TreeNode tn =

              AddTreeNode(root, "Block references", type);

            if (useEnum)

            {

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)refs.GetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(refs, useEnum, tn);

            }

          }

          break;

 

        case "AcSmAcDbBlockRecordReference":

          {

            AcSmAcDbBlockRecordReference brr =

              (AcSmAcDbBlockRecordReference)item;

 

            AddTreeNode(

              root,

              "Block record reference: " + brr.GetName(),

              type

            );

          }

          break;

 

        case "AcSmAcDbLayoutReference":

          {

            AcSmAcDbLayoutReference lr =

              (AcSmAcDbLayoutReference)item;

 

            AddTreeNode(

              root,

              "Layout reference: " + lr.GetName(),

              type

            );

          }

          break;

 

        case "AcSmFileReference":

          {

            AcSmFileReference fr = (AcSmFileReference)item;

 

            AddTreeNode(

              root,

              "Layout reference: " + fr.GetFileName(),

              type

            );

          }

          break;

 

        case "AcSmAcDbViewReference":

          {

            AcSmAcDbViewReference vr = (AcSmAcDbViewReference)item;

 

            AddTreeNode(

              root,

              "View reference: " + vr.GetName(),

              type

            );

          }

          break;

 

        case "AcSmObjectReference":

          {

            AcSmObjectReference or =

              (AcSmObjectReference)item;

 

            AddTreeNode(

              root,

              "Object reference: " +

              or.GetReferencedObject().GetTypeName(),

              type

            );

          }

          break;

 

        case "AcSmProjectPointLocations":

          {

            AcSmProjectPointLocations ppls =

              (AcSmProjectPointLocations)item;

 

            TreeNode tn =

              AddTreeNode(root, "ProjectPoint locations", type);

            if (useEnum)

            {

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)ppls.GetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(ppls, useEnum, tn);

            }

          }

          break;

 

        case "AcSmPublishOptions":

          {

            AcSmPublishOptions opts = (AcSmPublishOptions)item;

 

            AddTreeNode(root, "Publish options", type);

          }

          break;

 

        case "AcSmResources":

          {

            AcSmResources res = (AcSmResources)item;

 

            TreeNode tn = AddTreeNode(root, "Resources", type);

            if (useEnum)

            {

              IAcSmEnumComponent enumerator =

                (IAcSmEnumComponent)res.GetEnumerator();

              ProcessEnumerator(enumerator, useEnum, tn);

            }

            else

            {

              ProcessItems(res, useEnum, tn);

            }

          }

          break;

 

        default:

          {

            Document doc =

              acApp.Application.DocumentManager.MdiActiveDocument;

            Editor ed = doc.Editor;

            ed.WriteMessage("\nMissed Type = " + type);

          }

          break;

      }

    }

 

    private TreeNode AddTreeNode(

      TreeNode root, string name, string tooltip

    )

    {

      // Create a new node on the tree view with a tooltip

 

      TreeNode node = new TreeNode(name);

      node.ToolTipText = tooltip;

 

      // Add it to what we have

 

      root.Nodes.Add(node);

      return node;

    }

  }

}

The code uses two different approaches for parsing the data: the first is via enumerators retrieved from the objects themselves (used by the "Sheets View") and the second works purely via ownership ("Database View"). Each item's tooltip shows the underlying COM class, to help understand the structure. I do agree with Tony that this approach – as you can see from the above code – does start to get unwieldy. With any luck I'll provide a more streamlined approach to this in a future post in this series.

Let's see what happens when we run the updated SSTREE command:

Our Sheets View Our Database View

That's it for today. Now that Tony has clarified a mistaken assumption in my last post, I expect I'll take a look at implementing the UI via data-binding with WPF, when I get some time. This should also demonstrate the benefits of a more elegant approach to extracting and presenting the data.

5 responses to “Populating a tree view inside AutoCAD with sheet set data using .NET - Part 2”

  1. WOW looked at data binding with WPF, Talk about overly complicated! They better get better tools for WPF and data binding.

  2. I've been trying to rewrite this and the part 1 version in VB.net to no avail. I suspect there is a casting issue. When I try to cast the AcSmSheet.GetSheetViews into a IAcSmEnumComponent things go pear shaped, and nothing gets past. My tree has the sheet set with its subsets and no sheets.

    I've also tried to use C# but I can't get rid of the red line under UserControl1 and TreeNode.

    The short of what I'm trying to do is pull out all the information from the DST into something a little easier to navigate... do stuff to it... then put it back. Sounds easy as its just a whole bunch of text, but its turning out to be quite a task.

    I'm happy to post code/more information.

  3. Have you tried an automated conversion tool? I know these don't always provide perfect results, but they are usually of some help.

    I don't see where the results of GetSheetViews() is cast to IAcSmEnumComponent, above: it gets passes to ProcessItem() which expects an IAcSmPersist object.

    If the problem goes beyond a flat port of the above code, then I suggest contacting ADN or posting to the discussion group (as it's a bit beyond the scope of the help I can provide).

    Kean

  4. Hi Kean,

    I know its been years since you last updated this particular blog, I used the sample code above as a baseline for sheet set read/write programs and thank you for that, Question, is there a way to create a link between the sheet view and the specific category the sheet view is under ?

  5. Think i got it.

    case "AcSmSheetView":
    {
    AcSmSheetView sv = (AcSmSheetView)item;
    string svName = sv.GetName();
    if (!String.IsNullOrEmpty(svName))
    AddTreeNode(root, svName + " + " + sv.GetCategory().GetName()
    , type);
    }
    break;

    Thanks

Leave a Reply to Kean Walmsley Cancel reply

Your email address will not be published. Required fields are marked *