September 24, 2008

Creating a layer group inside AutoCAD using .NET

Your example above shows how to create a property type filter, how do you add layers to a group type filter (LayerGroup class)? I can create the group filter but can't figure out how to add layers to the group. The filterexpression method is available but doesn't seem to work (at least not like with the LayerFilter object)

To tackle this, let's start by looking at the documentation on the LayerGroup class in the .NET Reference (currently part of the ObjectARX Reference):

LayerGroup is derived from LayerFilter and serves as the access to layer group filters. It allows the client to specify and retrieve a set of layer IDs. The filter() method returns true if the object ID of the given layer is contained in the set of layer IDs for the LayerGroup.

Specifying the filter criteria is done solely by using LayerId. LayerGroup doesn't use a filter expression string, so FilterExpression and FilterExpressionTree return a null pointer.

The recommended way of identifying LayerGroup filters in a group of layer filters is to query the IsIdFilter property, which returns true for an LayerGroup.

So, at the root of the question, we need to create a LayerGroup and add the ObjectIds of the LayerTableRecords we wish to include in the group to its LayerIds property.

I started by taking the code in the post referred to above and extended it to contain a new CLG command to Create a Layer Group. This command lists the available layers for the user to select from, and creates a layer group containing the selected layers.

Here's the updated C# code - pay particular attention to the new CLG command, of course:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.LayerManager;

using System.Collections.Generic;

namespace LayerFilters

{

publicclassCommands

{

[CommandMethod("LLFS")]

staticpublicvoid ListLayerFilters()

{

Document doc =

Application.DocumentManager.MdiActiveDocument;

Database db = doc.Database;

Editor ed = doc.Editor;

// List the nested layer filters

LayerFilterCollection lfc =

db.LayerFilters.Root.NestedFilters;

for (int i = 0; i < lfc.Count; ++i)

{

LayerFilter lf = lfc[i];

ed.WriteMessage(

"\n{0} - {1} (can{2} be deleted)",

i + 1,

lf.Name,

(lf.AllowDelete ? "" : "not")

);

}

}

[CommandMethod("CLFS")]

staticpublicvoid CreateLayerFilters()

{

Document doc =

Application.DocumentManager.MdiActiveDocument;

Database db = doc.Database;

Editor ed = doc.Editor;

try

{

// Get the existing layer filters

// (we will add to them and set them back)

LayerFilterTree lft =

db.LayerFilters;

LayerFilterCollection lfc =

lft.Root.NestedFilters;

// Create three new layer filters

LayerFilter lf1 = newLayerFilter();

lf1.Name = "Unlocked Layers";

lf1.FilterExpression = "LOCKED==\"False\"";

LayerFilter lf2 = newLayerFilter();

lf2.Name = "White Layers";

lf2.FilterExpression = "COLOR==\"7\"";

LayerFilter lf3 = newLayerFilter();

lf3.Name = "Visible Layers";

lf3.FilterExpression =

"OFF==\"False\" AND FROZEN==\"False\"";

// Add them to the collection

lfc.Add(lf1);

lfc.Add(lf2);

lfc.Add(lf3);

// Set them back on the Database

db.LayerFilters = lft;

// List the layer filters, to see the new ones

ListLayerFilters();

}

catch (Exception ex)

{

ed.WriteMessage(

"\nException: {0}",

ex.Message

);

}

}

[CommandMethod("CLG")]

staticpublicvoid CreateLayerGroup()

{

Document doc =

Application.DocumentManager.MdiActiveDocument;

Database db = doc.Database;

Editor ed = doc.Editor;

// A list of the layers' names & IDs contained

// in the current database, sorted by layer name

SortedList<string, ObjectId> ld =

newSortedList<string, ObjectId>();

// A list of the selected layers' IDs

ObjectIdCollection lids =

newObjectIdCollection();

// Start by populating the list of names/IDs

// from the LayerTable

Transaction tr =

db.TransactionManager.StartTransaction();

using (tr)

{

LayerTable lt =

(LayerTable)tr.GetObject(

db.LayerTableId,

OpenMode.ForRead

);

foreach(ObjectId lid in lt)

{

LayerTableRecord ltr =

(LayerTableRecord)tr.GetObject(

lid,

OpenMode.ForRead

);

ld.Add(ltr.Name, lid);

}

}

// Display a numbered list of the available layers

ed.WriteMessage("\nLayers available for group:");

int i = 1;

foreach (KeyValuePair<string,ObjectId> kv in ld)

{

ed.WriteMessage(

"\n{0} - {1}",

i++,

kv.Key

);

}

// We will ask the user to select from the list

PromptIntegerOptions pio =

newPromptIntegerOptions(

"\nEnter number of layer to add: "

);

pio.LowerLimit = 1;

pio.UpperLimit = ld.Count;

pio.AllowNone = true;

// And will do so in a loop, waiting for

// Escape or Enter to terminate

PromptIntegerResult pir;

do

{

// Select one from the list

pir = ed.GetInteger(pio);

if (pir.Status == PromptStatus.OK)

{

// Get the layer's name

string ln =

ld.Keys[pir.Value-1];

// And then its ID

ObjectId lid;

ld.TryGetValue(ln, out lid);

// Add the layer'd ID to the list, is it's not

// already on it

if (lids.Contains(lid))

{

ed.WriteMessage(

"\nLayer \"{0}\" has already been selected.",

ln

);

}

else

{

lids.Add(lid);

ed.WriteMessage(

"\nAdded \"{0}\" to selected layers.",

ln

);

}

}

} while (pir.Status == PromptStatus.OK);

// Now we've selected our layers, let's create the group

try

{

if (lids.Count > 0)

{

// Get the existing layer filters

// (we will add to them and set them back)

LayerFilterTree lft =

db.LayerFilters;

LayerFilterCollection lfc =

lft.Root.NestedFilters;

// Create a new layer group

LayerGroup lg = newLayerGroup();

lg.Name = "My Layer Group";

// Add our layers' IDs to the list

foreach (ObjectId id in lids)

lg.LayerIds.Add(id);

// Add the group to the collection

lfc.Add(lg);

// Set them back on the Database

db.LayerFilters = lft;

ed.WriteMessage(

"\n\"{0}\" group created containing {1} layers.\n",

lg.Name,

lids.Count

);

// List the layer filters, to see the new group

ListLayerFilters();

}

}

catch (Exception ex)

{

ed.WriteMessage(

"\nException: {0}",

ex.Message

);

}

}

[CommandMethod("DLF")]

staticpublicvoid DeleteLayerFilter()

{

Document doc =

Application.DocumentManager.MdiActiveDocument;

Database db = doc.Database;

Editor ed = doc.Editor;

ListLayerFilters();

try

{

// Get the existing layer filters

// (we will add to them and set them back)

LayerFilterTree lft =

db.LayerFilters;

LayerFilterCollection lfc =

lft.Root.NestedFilters;

// Prompt for the index of the filter to delete

PromptIntegerOptions pio =

newPromptIntegerOptions(

"\n\nEnter index of filter to delete"

);

pio.LowerLimit = 1;

pio.UpperLimit = lfc.Count;

PromptIntegerResult pir =

ed.GetInteger(pio);

// Get the selected filter

LayerFilter lf = lfc[pir.Value - 1];

// If it's possible to delete it, do so

if (!lf.AllowDelete)

{

ed.WriteMessage(

"\nLayer filter cannot be deleted."

);

}

else

{

lfc.Remove(lf);

db.LayerFilters = lft;

ListLayerFilters();

}

}

catch(Exception ex)

{

ed.WriteMessage(

"\nException: {0}",

ex.Message

);

}

}

}

}

Here's what happens when we run the code on a drawing containing a number of layers (all of which have the default properties for new layers):

Command: CLG

Layers available for group:

1 - 0

2 - Layer1

3 - Layer2

4 - Layer3

5 - Layer4

6 - Layer5

7 - Layer6

8 - Layer7

9 - Layer8

10 - Layer9

11 - Test1

12 - Test2

13 - Test3

14 - Test4

15 - Test5

Enter number of layer to add: 2

Added "Layer1" to selected layers.

Enter number of layer to add: 4

Added "Layer3" to selected layers.

Enter number of layer to add: 6

Added "Layer5" to selected layers.

Enter number of layer to add: 8

Added "Layer7" to selected layers.

Enter number of layer to add: 11

Added "Test1" to selected layers.

Enter number of layer to add: 15

Added "Test5" to selected layers.

Enter number of layer to add:

"My Layer Group" group created containing 6 layers.

1 - My Layer Group (can be deleted)

2 - All Used Layers (cannot be deleted)

3 - Unreconciled New Layers (cannot be deleted)

4 - Viewport Overrides (cannot be deleted)

I've hard-coded the name of the new layer group to be "My Layer Group". It's a trivial exercise to modify the code to ask the user to enter a name, each time.