A big thank you to Fenton Webb, from DevTech Americas, for providing the code which was the basis for this post. Thanks, Fents! :-)
Fenton sent a version of this code recently to an ADN member who was interested in duplicating the information shown in AutoCAD’s Sheet Set Manager inside a custom, palette-hosted tree-view dialog. Fenton’s version made use of WPF: I’ve dumbed it down a little to use WinForms, but may do a follow-up post using WPF (although the WPF TreeView doesn’t appear to support data-binding, so I may well decide not to bother).
I have made some other changes to Fenton’s code, and if there are any bugs they’re likely to be mine rather than his. I fleshed out some of the various item types, but as I haven’t yet been able to see them in action (I’m not even sure there was any real need to do so), they may need some further work or even stripping out.
Anyway, here’s the C# code. To make it work your project will need a reference to acsmcomponents18.tlb (or acsmcomponents17.tlb if using AutoCAD 2007-2009) as well as AcMgd.dll, AcDbMgd.dll and the standard .NET components that Visual Studio may be looking for when building the project.
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 userControl = null;
[CommandMethod("SSTREE")]
public void PopulateCustomSheetTree()
{
// Check the state of the paletteset
if (ps == null)
{
// Then create it
ps = new PaletteSet("Custom Sheet Tree");
userControl = new UserControl1();
ps.Add("MySheetView", userControl);
}
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);
AcSmSheetSet ss = db.GetSheetSet();
// Create a root item in the TreeView,
// the name of the SheetSet
TreeNode root = new TreeNode(ss.GetName());
userControl.treeView1.Nodes.Add(root);
ProcessEnumerator(ss.GetSheetEnumerator(), root);
db.UnlockDb(db, true);
mgr.Close(db);
}
void ProcessEnumerator(IAcSmEnumComponent iter, TreeNode root)
{
IAcSmComponent item = iter.Next();
while (item != null)
{
string type = item.GetTypeName();
switch (type)
{
case "AcSmSubset":
try
{
AcSmSubset subset = (AcSmSubset)item;
string subName = subset.GetName();
if (!String.IsNullOrEmpty(subName))
{
TreeNode tn = AddTreeNode(root, subName);
IAcSmEnumComponent enumerator =
(IAcSmEnumComponent)subset.GetSheetEnumerator();
ProcessEnumerator(enumerator, tn);
}
}
catch { }
break;
case "AcSmSheet":
try
{
AcSmSheet sh = (AcSmSheet)item;
string shName = sh.GetName();
if (!String.IsNullOrEmpty(shName))
{
TreeNode tn = AddTreeNode(root, shName);
IAcSmEnumComponent enumerator =
(IAcSmEnumComponent)sh.GetSheetViews();
ProcessEnumerator(enumerator, tn);
}
}
catch { }
break;
case "AcSmSheetViews":
try
{
AcSmSheet sh = (AcSmSheet)item;
string shName = sh.GetName();
if (!String.IsNullOrEmpty(shName))
{
TreeNode tn = AddTreeNode(root, shName);
IAcSmEnumComponent enumerator =
(IAcSmEnumComponent)sh.GetSheetViews();
ProcessEnumerator(enumerator, tn);
}
}
catch { }
break;
case "AcSmSheetView":
try
{
AcSmSheetView sv = (AcSmSheetView)item;
string svName = sv.GetName();
if (!String.IsNullOrEmpty(svName))
AddTreeNode(root, svName);
}
catch { }
break;
case "AcSmCustomPropertyValue":
AcSmCustomPropertyValue pv =
(AcSmCustomPropertyValue)item;
AddTreeNode(
root,
"Custom property value: " + pv.GetValue().ToString()
);
break;
case "AcSmObjectReference":
AcSmObjectReference or =
(AcSmObjectReference)item;
AddTreeNode(
root,
"Object reference: " +
or.GetReferencedObject().GetTypeName()
);
break;
case "AcSmCustomPropertyBag":
try
{
AcSmCustomPropertyBag bag =
(AcSmCustomPropertyBag)item;
TreeNode tn = AddTreeNode(root, "Custom property bag");
IAcSmEnumComponent enumerator =
(IAcSmEnumComponent)bag.GetPropertyEnumerator();
ProcessEnumerator(enumerator, tn);
}
catch { }
break;
case "AcSmAcDbLayoutReference":
AcSmAcDbLayoutReference lr =
(AcSmAcDbLayoutReference)item;
AddTreeNode(
root,
"Layout reference: " + lr.GetName()
);
break;
case "AcSmFileReference":
AcSmFileReference fr = (AcSmFileReference)item;
AddTreeNode(
root,
"Layout reference: " + fr.GetFileName()
);
break;
case "AcSmAcDbViewReference":
AcSmAcDbViewReference vr = (AcSmAcDbViewReference)item;
AddTreeNode(
root,
"View reference: " + vr.GetName()
);
break;
case "AcSmResources":
try
{
AcSmResources res = (AcSmResources)item;
TreeNode tn = AddTreeNode(root, "Resources");
IAcSmEnumComponent enumerator =
(IAcSmEnumComponent)res.GetEnumerator();
ProcessEnumerator(enumerator, tn);
}
catch { }
break;
default:
Document doc =
acApp.Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
ed.WriteMessage("\nMissed Type = " + type);
break;
}
item = iter.Next();
}
}
private TreeNode AddTreeNode(TreeNode root, string name)
{
// Create a new node on the tree view
TreeNode node = new TreeNode(name);
// Add it to what we have
root.Nodes.Add(node);
return node;
}
}
}
You’ll need to add a new UserControl to your project and a TreeView to that, setting the Dock property to fill. Be sure to keep the default names as UserControl1 and treeView1. Other than than, no UI work is needed.
Here’s what happens when we run the SSTREE command along and load the hard-coded DST file into the SheetSet Manager (displayed with the SSM command) for comparison:
Clearly more could be done to make the UI look the same (icons, etc.), but this post is more about recursively parsing a SheetSet’s tree rather than anything else. If you come across any issues as you try this out with other data stored in DST files, be sure to post a comment.