2016年1月29日

Unlocking layers for certain AutoCAD commands using .NET

Unlocking layers for certain AutoCAD commands using .NET

Unlocking layers for certain AutoCAD commands using .NET

This question came up during last week's accelerator, and is part of the reason I spent creating the last post: is it possible to selectively unlock certain layers for the duration of commands that have been specified by the user? Let's take an example: you have layers that should remain locked, apart from when using the MOVE command (COPY and ERASE should not work).

The approach I took was to maintain a dictionary mapping command names to lists of layers to unlock. When a command is launched – which we can tell using the Document.CommandWillStart event – we check whether it has layers associated with it. If so, we unlock them for the duration of the command and lock them again on command completion.

I knew we'd want a map per document, so I went and used the approach from this post. Something I think people will find useful: rather than maintaining separate code to check for documents being created and closed, I used the "doc data" class itself to attach the command-tracking event handlers to documents when they're opened, and its "dispose" method to remove them when closed. This makes the code much simpler, I'm sure you'll agree.

Here's the C# code that implements the UFC command, as well as having the LL and UL commands (that use the same, slightly modified extension method) we saw last time.

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System;

using System.Collections;

using System.Collections.Generic;

using System.Runtime.InteropServices;

 

[assembly: PerDocumentClass(typeof(LayerManipulation.UnlockCommands))]

 

namespace LayerManipulation

{

  public class UnlockCommands : System.IDisposable

  {

    public const string RecordName = "TtifLayerUnlockData";

 

    // Map commands to layers to unlock and relock

 

    public Dictionary<string, ObjectIdCollection> CommandMap =

      new Dictionary<string, ObjectIdCollection>();

 

    private Document _doc = null;

    private bool _disposed = false;

 

    // Constructor

 

    public UnlockCommands(Document doc)

    {

      _doc = doc;

 

      doc.UserData.Add(RecordName, this);

 

      // Add our command handlers

 

      doc.CommandWillStart += OnCommandWillStart;

      doc.CommandEnded += OnCommandEnded;

      doc.CommandCancelled += OnCommandEnded;

      doc.CommandFailed += OnCommandEnded;

    }

 

    void OnCommandWillStart(object sender, CommandEventArgs e)

    {

      var doc = (Document)sender;

      var uc = doc.UserData[UnlockCommands.RecordName] as UnlockCommands;

 

      // If the command ending is in our list, unlock the layer(s)

 

      if (uc != null && uc.CommandMap.ContainsKey(e.GlobalCommandName))

      {

        doc.LockOrUnlockLayers(false, uc.CommandMap[e.GlobalCommandName], false);

      }

    }

 

    void OnCommandEnded(object sender, CommandEventArgs e)

    {

      var doc = (Document)sender;

      var uc = doc.UserData[UnlockCommands.RecordName] as UnlockCommands;

 

      // If the command ending is in our list, relock the layer(s)

 

      if (uc != null && uc.CommandMap.ContainsKey(e.GlobalCommandName))

      {

        doc.LockOrUnlockLayers(true, uc.CommandMap[e.GlobalCommandName], false);

      }

    }

 

    public void Dispose()

    {

      Dispose(true);

      GC.SuppressFinalize(this);          

    }

 

    protected virtual void Dispose(bool disposing)

    {

      if (_disposed)

        return;

 

      // Remove our command handlers

 

      _doc.CommandWillStart -= OnCommandWillStart;

      _doc.CommandEnded -= OnCommandEnded;

      _doc.CommandCancelled -= OnCommandEnded;

      _doc.CommandFailed += OnCommandEnded;

 

      _disposed = true;

    }

  }

 

  public static class Extensions

  {

    public static void LockOrUnlockLayers(

      this Document doc, bool dolock,

      ObjectIdCollection layers = null, bool ignoreCurrent = true,

      bool lockZero = false

    )

    {

      var db = doc.Database;

      var ed = doc.Editor;

 

      using (var tr = db.TransactionManager.StartTransaction())

      {

        var layerIds =

          (layers != null ? (IEnumerable)layers :

            (IEnumerable)tr.GetObject(db.LayerTableId, OpenMode.ForRead));

 

        foreach (ObjectId ltrId in layerIds)

        {

          // Don't try to lock/unlock either the current layer or layer 0

          // (depending on whether lockZero == true for the latter)

 

          if (

            (!ignoreCurrent || ltrId != db.Clayer) &&

            (lockZero || ltrId != db.LayerZero)

          )

          {

            // Open the layer for write and lock/unlock it

 

            var ltr = (LayerTableRecord)tr.GetObject(ltrId, OpenMode.ForWrite);

            ltr.IsLocked = dolock;

            ltr.IsOff = ltr.IsOff; // This is needed to force a graphics update

          }

        }

        tr.Commit();

      }

 

      // These two calls will result in the layer's geometry fading/unfading

      // appropriately

 

      ed.ApplyCurDwgLayerTableChanges();

      ed.Regen();

    }

 

    public static ObjectIdCollection SelectLayers(this Document doc)

    {

      var db = doc.Database;

      var ed = doc.Editor;

 

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

      // in the current database, sorted by layer name

 

      var ld = new SortedList<string, ObjectId>();

 

      // A list of the selected layers' IDs

 

      var lids = new ObjectIdCollection();

 

      // Start by populating the list of names/IDs

      // from the LayerTable

 

      using (var tr = db.TransactionManager.StartOpenCloseTransaction())

      {

        var lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);

        foreach (ObjectId lid in lt)

        {

          var ltr = (LayerTableRecord)tr.GetObject(lid, OpenMode.ForRead);

          ld.Add(ltr.Name, lid);

        }

      }

 

      // Display a numbered list of the available layers

 

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

 

      ed.SelectNamedLayers(ld, lids);

 

      return lids;

    }

 

    private static void SelectNamedLayers(

      this Editor ed,

      SortedList<string, ObjectId> ld,

      ObjectIdCollection lids

    )

    {

      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

 

      var pio = new PromptIntegerOptions("\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, if 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);

    }

  }

 

  public class Commands

  {

    [CommandMethod("LL")]

    public void LockLayers()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

 

      doc.LockOrUnlockLayers(true);

    }

 

    [CommandMethod("UL")]

    public void UnlockLayers()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

 

      doc.LockOrUnlockLayers(false);

    }

 

    [CommandMethod("UFC")]

    public void UnlockForCommands()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

      var ed = doc.Editor;

 

      // Store the selected commands in a list

 

      var cmds = new List<string>();

 

      PromptResult pr;

      bool cmdEntered;

      do

      {

        pr = ed.GetString("\nEnter name of command for which to unlock layers");

        cmdEntered =

          pr.Status == PromptStatus.OK && !String.IsNullOrEmpty(pr.StringResult);

        if (cmdEntered)

          cmds.Add(pr.StringResult.ToUpper());

      }

      while (cmdEntered);

 

      if (pr.Status == PromptStatus.Cancel)

        return;

 

      var ids = doc.SelectLayers();

 

      var uc = doc.UserData[UnlockCommands.RecordName] as UnlockCommands;

      foreach (string cmd in cmds)

      {

        if (uc.CommandMap.ContainsKey(cmd))

        {

          var existing = uc.CommandMap[cmd];

          foreach (ObjectId id in ids)

          {

            if (!existing.Contains(id))

            {

              existing.Add(id);

            }

          }

        }

        else

        {

          uc.CommandMap.Add(cmd, ids);

        }

      }

 

      if (cmds.Count > 0 && ids.Count > 0)

      {

        ed.WriteMessage(

          "\n{0} command{1} will now unlock {2} layers.",

          cmds.Count, cmds.Count == 1 ? "" : "s", ids.Count

        );

      }

    }

  }

}

 

Here's the UFC command in action:

Unlock for commands

You can see that when we MOVE the layers unlock – relocking again, afterwards – but the same doesn't happen for COPY.

留下您的评论