Home .NET Creating plugins for AutoCAD using.NET API (Part 3 – Working with Layers)

Creating plugins for AutoCAD using.NET API (Part 3 – Working with Layers)

by admin

This is another article from series dedicated to the development of plug-ins for AutoCAD.It will talk about basic operations with layers in a document.

public static string disclaimer = "The author is not a professional developer and has no deep knowledge of AutoCAD.This post is just a short story about creating a plugin.";

1.General information

Everyone reading this article probably knows what a layer is. At a rudimentary level, it can be thought of as a container for drawing elements that have some commonfeature that is important to the designer.
Layers can greatly simplify life not only for the engineer creating the drawing, but also for the programmer. For example, all the elements on one layer can be hidden very quickly, if necessary, and then displayed again on the workspace.
For the purposes of this article, we will need some basic information, which can mostly be gleaned from from here ( mirror )

1.1. Layer number zero

Every AutoCAD document always contains at least one layer-zero (named "0")that cannot be deleted. This layer is often very useful when, for example, you delete other layers.

1.2 Current layer

One of the layers in the document must be the current layer. All objects added to the drawing are placed on it (primitive objects, at least – with blocks, things are a bit more complicated).

1.3. Layer properties

Any (including zero) layer in AutoCAD has three properties :

  • included (" on ");
  • frozen (" freeze ");
  • blocked (" lock ").

All of these properties are binary (each can be considered a yes/no flag).
Flag " included " is responsible for the visibility of the layer. If this flag is cleared, all elements on the layer are no longer visible in the drawing.
Flag " frozen " is also responsible for the visibility of the layer. It has the same effect as the "on" flag. As stated in the documentation, this property improves performance on very large drawings.
NB: Personally, I have never worked with large drawings and have never used this feature.
Flag " blocked " is responsible for the ability to edit the layer. If this flag is set, all the elements located on the layer become inaccessible for any changes (moving, deleting, etc.).In addition, no new elements can be added to the locked layer.
You can control the properties of layers from within AutoCAD. In the illustration below, the "Layers" panel is highlighted in poison green, and the drop-down list of layers is highlighted in purple, with property switches (light bulb, sunshine, open lock – "on", "freeze" and "lock" respectively) on the left side.
NB: You should really pay attention to these switches: when you write a plugin, you don’t always get it right the first time, and sometimes you have to use "manual control".
NB: Some sites claim that the colors in this picture are not green and purple, but lime and heliotrope.

I must be dog

1.4. Layer management

The simplest operations with layers were discussed in the previous paragraph. For more complex operations (e.g., creating or removing a layer) you should use the "Layer Properties" panel, which you call with the command LAYER or by clicking the appropriate icon in the "Layers" panel (orange in the figure in the previous subsection). This is what this panel looks like :
Creating plugins for AutoCAD using.NET API (Part 3 - Working with Layers)
Of particular interest is the framed button section. The first button in it creates a new layer, the third one deletes an existing layer, if possible.
Cannot be deleted :

  • zero layer;
  • current layer;
  • layer containing at least one object;
  • layer referenced in the block definition.

Ensure that these conditions are met before removing the layer.
NB: Actually the list of layers not allowed to be removed is a bit longer :
Creating plugins for AutoCAD using.NET API (Part 3 - Working with Layers)
Unfortunately, the author of this post has not encountered any Defpoints nor with Xref-dependent layers , therefore we will leave it to our readers to work through these elementary questions as uncomplicated homework. ^__^
That’s it with the theory, you can get down to practice.

2. Writing a plugin

2.1. Creating a new plugin project

This was the subject of first article cycle. The required version of the .NET Framework in the examples below is NET Framework 3.5
You can add a code frame right away :

usingSystem;using Autodesk.AutoCAD.Runtime;using Autodesk.Windows;namespace MyAutoCADDll{public class Commands : IExtensionApplication{// this function will be called when "TestCommandquot command is executed in AutoCAD;[CommandMethod("TestCommand")]public void MyCommand(){}// The Initialize() and Terminate() functions are necessary to implement the IExtensionApplication interfacepublic void Initialize(){}public void Terminate(){}}}

2.2. Adding links to the required libraries

In this example, we need two AutoCAD .NET API libraries: AcMgd.dll and AcDbMgd.dll (as always, do not forget to turn off CopyLocal ).

2.3. Adding a new layer

All the layers present in the document are stored in the Layer List. To add a new layer to the drawing it is enough to add a new element to this list.
However, despite the simplicity of the operation, you get quite a lot of code.
Code :

using System;using System.IO;using Autodesk.AutoCAD.Runtime;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;namespace HabrPlug_Layers{public class Commands : IExtensionApplication{// this function will be called when the command "TestCommandquot is executed in AutoCAD;[CommandMethod("TestCommand")]public void MyCommand(){// get the current document and its databaseDocument acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;Database acCurDb = acDoc.Database;//block the documentusing (DocumentLockdocloc = acDoc.LockDocument()){// start a transactionusing (Transaction tr = acCurDb.TransactionManager.StartTransaction()){// open the table of document layersLayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable;// create a new layer and name itLayerTableRecord acLyrTblRec = new LayerTableRecord();acLyrTblRec.Name = "HabrLayer";// add the created layer to the layer tableacLyrTbl.Add(acLyrTblRec);// add the created layer to the documenttr.AddNewlyCreatedDBObject(acLyrTblRec, true);// commit the transactiontr.Commit();}}}// The Initialize() and Terminate() functions are necessary to implement the IExtensionApplication interfacepublic void Initialize(){}public void Terminate(){}}}

After loading the plugin and executing the "TestCommand" a new layer should appear in the drawing :
Creating plugins for AutoCAD using.NET API (Part 3 - Working with Layers)
The above code used two important constructs that are often encountered when working with drawing objects :
(a) Document blocking;

using (DocumentLockdocloc = acDoc.LockDocument())

Autocad documentation says that requests to modify objects or access AutoCAD can be made in any context and from any number of applications. To prevent conflicts with other requests, the programmer himself must ensure that the document is locked before changing it. Failure to do so will, in some cases, cause an error when modifying the document database.
After all the necessary changes have been made to the database, it must be unlocked using the Dispose() of the object DocumentLock You can also use the block using with the declaration of the object DocumentLock – in this case, when you exit the block, the database is automatically unlocked. ( source , mirror )
NB: In my experience, using the construct using is much more convenient, because it reduces to zero the probability of forgetting to call the Dispose() b) transaction call.

using (Transaction tr = acCurDb.TransactionManager.StartTransaction())…tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable;...tr.AddNewlyCreatedDBObject(acLyrTblRec, true);...tr.Commit();

In documentation ( mirror ) states that when working with objects such as segments, circles, and polylines, or with an identifier table and its records, you must open the object for reading or writing.
Trying to ignore this requirement will not result in anything good – very unpleasant (also from the debugging point of view) errors may occur.
We can distinguish several basic steps of the transaction :

  1. start of a new transaction;
  2. opening an objectfor reading or writing;
  3. if the object is created for the first time (it is not yet in the document) – adding it to the document database;
  4. transaction commit;
  5. transaction termination.

All of these items are discussed in detail in the documentation at the links above.
As in the case of DocumentLock , it is necessary to either call the method Dispose() after finishing a transaction, or use the using
Now that we understand document locking and transactions, we can move on to parsing the rest of the code.
Firstly, we plug in the necessary namespaces :
Autodesk.AutoCAD.Runtime – to use the type IExtensionApplication and the construct CommandMethod ;
Autodesk.AutoCAD.DatabaseServices – to use the types Document and DocumentLock ;
Autodesk.AutoCAD.Runtime – to use the types Database , Transaction , LayerTable , OpenMode and LayerTableRecord
Second, we lock the document and start the transaction :

// get the current document and its databaseDocument acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;Database acCurDb = acDoc.Database;//block the documentusing(DocumentLock docloc = acDoc.LockDocument()){// start a transactionusing(Transaction tr = acCurDb.TransactionManager.StartTransaction()){

Third, we get a link to the document layer table :

LayerTableacLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable;

Method GetObject() accepts two parameters as input: the Id of the object to be opened and the access level.The Id of the table of document layers can be found out with the property LayerTableId of the database of this document.Since we want to change the layer table (add a new layer to it), we use the access level "record" ( OpenMode.ForWrite ).Finally, the method GetObject() returns a value of type "object" ( object ), which should be explicitly converted to the type we need ( LayerTable ).
Fourth, we create a new layer :

LayerTableRecordacLyrTblRec = new LayerTableRecord();acLyrTblRec.Name = "HabrLayer";

The layer (or rather, its corresponding entry in the document layer table) has the type LayerTableRecord Its constructor doesn’t take any values, so all initialization has to be done after creation.
In this example we have changed only the name of the new layer, but if necessary we can set other parameters, such as visibility (property IsOff ) or availability for editing (property IsLocked ).
Fifth, we put the new layer in the layer table of the document :

acLyrTbl.Add(acLyrTblRec);

Layer table is a class for which the interface IEnumerable To add a new element, the method Add()
Sixth, we add a new layer to the document database :

tr.AddNewlyCreatedDBObject(acLyrTblRec, true);

Since this layer did not exist before the transaction, we must explicitly state that it must be added to the document database. If this is not done, the record will not be added.
Important detail : function AddNewlyCreatedDBObject should be called after adding a layer to the document’s layer table, not before. If you try to swap these operations, the layer will not be added. I don’t have a coherent logical explanation for this; I’d be glad if knowledgeable people could share a clue in the comments.
Seventh, we fix the transaction :

tr.Commit();

If any items opened during a transaction using the GetObject() were modified, the transaction must be committed in order to save those changes to the document database by calling the Commit() Otherwise, no changes will be saved, and the document will remain in the state that existed at the start of the transaction.
Finally, we end the transaction and unlock the document :

}}

Here it is, an important perk of using the design using ! Without it, it’s very easy to forget to call the Dispose() all the more so because you have to do it twice – to commit the transaction and to unlock the document. And when using the using method Dispose() is called automatically.

2.4. Setting the current layer

You can get the current document layer and set a new one by using the property Clayer document database.
Code :

acCurDb .Clayer = acLyrTbl["HabrLayer"];

It is enough to insert this line in the previous example, just before the transaction is committed.

2.5. Changing properties and renaming a layer

To change the properties of a layer, open the corresponding entry in the layer table.
Code :

using System;using System.IO;using Autodesk.AutoCAD.Runtime;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;namespace HabrPlug_Layers{public class Commands : IExtensionApplication{// this function will be called when the command "TestCommandquot is executed in AutoCAD;[CommandMethod("TestCommand")]public void MyCommand(){// get the current document and its databaseDocument acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;Database acCurDb = acDoc.Database;//block the documentusing (DocumentLock docloc = acDoc.LockDocument()){// start a transactionusing (Transaction tr = acCurDb.TransactionManager.StartTransaction()){// open the table of document layersLayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable;// create a new layer and name itLayerTableRecord acLyrTblRec = new LayerTableRecord();acLyrTblRec.Name = "HabrLayer";// add the created layer to the layer tableacLyrTbl.Add(acLyrTblRec);// add the created layer to the documenttr.AddNewlyCreatedDBObject(acLyrTblRec, true);// commit the transactiontr.Commit();}}}// this function will be called when the "NewCommandquot command is executed in AutoCAD;[CommandMethod("NewCommand")]public void NewCommand(){// get the current document and its databaseDocument acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;Database acCurDb = acDoc.Database;//block the documentusing (DocumentLock docloc = acDoc.LockDocument()){// start a transactionusing (Transaction tr = acCurDb.TransactionManager.StartTransaction()){// open the table of document layersLayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable;// if our layer is not in the layer table - stop the commandif (acLyrTbl.Has("HabrLayer") == false){return;}// get the record of the layer to be modifiedLayerTableRecord acLyrTblRec = tr.GetObject(acLyrTbl["HabrLayer"], OpenMode.ForWrite) as LayerTableRecord;//hide and lock the layeracLyrTblRec.Name = "test";acLyrTblRec.IsOff = true;acLyrTblRec.IsLocked = true;// commit the transactiontr.Commit();}}}// The Initialize() and Terminate() functions are necessary to implement the IExtensionApplication interfacepublic void Initialize(){}public void Terminate(){}}}

Before operating on the layer, it is a good idea to make sure it exists using the Has() of the document layer table.
If we load the plugin, run the "TestCommand" command, and then run the "NewCommand" command, we see that the layer we created is now hidden, locked, and has changed its name. No one can find it now.
Creating plugins for AutoCAD using.NET API (Part 3 - Working with Layers)

2.6. Deleting a layer

Deleting a layer is done in much the same way as changing it. For the corresponding entry in the layer table, the method Erase() as follows :

acLyrTblRec.Erase(true);

However, it is important here to keep in mind the limitations discussed in the first section. It is not possible to remove :

  • zero layer;
  • current layer;
  • layer containing at least one object;
  • layer referenced in the block definition.

If you want to delete a layer that is currently current, you must make some other layer current before deleting it – for example, layer zero.
If you want to delete a layer that has some objects on it, you must remove all those objects or move them to another layer before deleting the layer.
Read more about removing layers in the documentation (section " Create and Edit AutoCAD Entities > Use Layers, Colors, and Linetypes > Work with Layers > Erase Layers ").
And Kean Walmsley’s blog has template to search for all objects on the layer. Based on this example, you can write a function that removes all objects from a layer.
In any case, before deleting a layer it is a good idea to make sure that the operation is correct, and to frame the deletion itself with the construct try ... catch
Code :

using System;using System.IO;using Autodesk.AutoCAD.Runtime;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;namespace HabrPlug_Layers{public class Commands : IExtensionApplication{// this function will be called when the command "TestCommandquot is executed in AutoCAD;[CommandMethod("TestCommand")]public void MyCommand(){// get the current document and its databaseDocument acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;Database acCurDb = acDoc.Database;//block the documentusing (DocumentLock docloc = acDoc.LockDocument()){// start a transactionusing (Transaction tr = acCurDb.TransactionManager.StartTransaction()){// open the table of document layersLayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable;// create a new layer and name itLayerTableRecord acLyrTblRec = new LayerTableRecord();acLyrTblRec.Name = "HabrLayer";// add the created layer to the layer tableacLyrTbl.Add(acLyrTblRec);// add the created layer to the documenttr.AddNewlyCreatedDBObject(acLyrTblRec, true);// commit the transactiontr.Commit();}}}// this function will be called when the "TestCommandquot command is executed in AutoCAD;[CommandMethod("NewCommand")]public void NewCommand(){// get the current document and its databaseDocument acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;Database acCurDb = acDoc.Database;//block the documentusing (DocumentLock docloc = acDoc.LockDocument()){// start a transactionusing (Transaction tr = acCurDb.TransactionManager.StartTransaction()){// open the table of document layersLayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable;// if our layer is not in the layer table - stop the commandif (acLyrTbl.Has("HabrLayer") == false){return;}// set the zero layer as the current layeracCurDb.Clayer = acLyrTbl["0"];// make sure that the deleted layer is not referenced by other objectsObjectIdCollection acObjIdColl = new ObjectIdCollection();acObjIdColl.Add(acLyrTbl["HabrLayer"]);acCurDb.Purge(acObjIdColl);if (acObjIdColl.Count > 0){// get the layer record to changeLayerTableRecord acLyrTblRec = tr.GetObject(acObjIdColl[0], OpenMode.ForWrite) as LayerTableRecord;try{// remove the layeracLyrTblRec.Erase(true);// commit the transactiontr.Commit();}catch (Autodesk.AutoCAD.Runtime.Exception Ex){// if there was an error, the layer cannot be deletedApplication.ShowAlertDialog("Error :n" + Ex.Message);}}}}}// The Initialize() and Terminate()functions are needed to implement the IExtensionApplication interfacepublic void Initialize(){}public void Terminate(){}}}

In this code, we already know everything except the method Purge() How it works is described in documentation :it parses the collection of objects and outputs only those objects which are not referenced by anyone.
In the example above, the method Purge() is called for a collection that contains only one object-the layer to be deleted. If this layer is not referenced, after calling the Purge() it will remain in the collection, and then you can proceed with removal. If, on the other hand, calling the Purge() returns an empty collection, then the layer to be deleted is referenced, probably by objects placed on it.
If we load the plugin and run the "TestCommand" and then run the "NewCommand" command, we see that the layer we created is removed from the drawing.
Creating plugins for AutoCAD using.NET API (Part 3 - Working with Layers)

2.7. Prohibit darkening of blocked layers

We would like to end with one more parameter related to layers.
By default, locked layers in AutoCAD appear dimmed. The maximum possible darkening is 90% (like the rectangle in the figure below).
Creating plugins for AutoCAD using.NET API (Part 3 - Working with Layers)
To change the degree of dimming, the variable LAYLOCKFADECTL Its value is set with the corresponding slider on the Layer Control panel (circled in green in the figure).
To change the value of a variable LAYLOCKFADECTL inside the plugin, you can use the function SetSystemVariable :

Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("LAYLOCKFADECTL", 0);

Executing this code will disable the darkening of the blocked layers.
Before changing a variable LAYLOCKFADECTL it is worth saving its current value so that it can be reset after the plugin has finished working.
Code :

using System;using System.IO;using Autodesk.AutoCAD.Runtime;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;namespace HabrPlug_Layers{public class Commands : IExtensionApplication{// a variable to store the old value of the blackout of the locked layersint old_LAYLOCKFADECTL = 0;//actions upon loading the pluginpublic void Initialize(){// store the current value of the blackout of the locked layersold_LAYLOCKFADECTL = System.Convert.ToInt32(Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("LAYLOCKFADECTL"));// set the new value of the blackout of the locked layers to 0Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("LAYLOCKFADECTL", 0);}//actions when AutoCAD is closedpublic void Terminate(){//returning the value of the blocked layers dimming that was set before the plugin was loadedAutodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("LAYLOCKFADECTL", old_LAYLOCKFADECTL);}}}

Unfortunately, the method Terminate() method does not always work correctly, and the initial value may not be restored. But at least we tried).
That’s all for now. Next time I will write about creating simple objects and blocks.

You may also like