Author Topic: Using Hard pointers to manage layers  (Read 7562 times)

0 Members and 1 Guest are viewing this topic.

sovby

  • Guest
Using Hard pointers to manage layers
« on: April 21, 2016, 10:57:12 PM »
I have a user at my office who purges Autocad drawings with layers that i want to keep in the drawings. Someone in the Augi forum suggested using hard pointers to keep the layers from being removed. Has anyone done anything similar to this? I do not have much experience using xrecords. Here is a sample from the forum post. "A hard pointer should prevent the pointed-to object (layer, in this case) from being purged. The pointer would reside under an XRecord in an Extension Dictionary, possibly attached to the Layers collection or the drawings named object dictionary." Would this work & if so how would i go about doing this?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #1 on: April 22, 2016, 02:15:32 AM »
Hi,

You can try these extension methods I wrote some time ago.

Code - C#: [Select]
  1.     public static class Extensions
  2.     {
  3.         // Opens the object for read.
  4.         public static T GetObject<T>(this ObjectId id) where T : DBObject
  5.         {
  6.             return (T)id.GetObject(OpenMode.ForRead, false, false);
  7.         }
  8.  
  9.         // Opens the object in the specified mode
  10.         public static T GetObject<T>(this ObjectId id, OpenMode mode) where T : DBObject
  11.         {
  12.             return (T)id.GetObject(mode, false, false);
  13.         }
  14.  
  15.         // Gets the object extension dictionary (returns null if not found)
  16.         public static DBDictionary GetExtensionDictionary(this DBObject obj)
  17.         {
  18.             ObjectId dictId = obj.ExtensionDictionary;
  19.             if (dictId == ObjectId.Null)
  20.             {
  21.                 return null;
  22.             }
  23.             return dictId.GetObject<DBDictionary>();
  24.         }
  25.  
  26.         // Gets the object extension dictionary (creates it if not already exists)
  27.         public static DBDictionary GetOrCreateExtensionDictionary(this DBObject obj)
  28.         {
  29.             if (obj.ExtensionDictionary == ObjectId.Null)
  30.             {
  31.                 obj.UpgradeOpen();
  32.                 obj.CreateExtensionDictionary();
  33.             }
  34.             return obj.ExtensionDictionary.GetObject<DBDictionary>();
  35.         }
  36.  
  37.         // Gets the xrecord data
  38.         public static ResultBuffer GetXrecordData(this DBDictionary dict, string key)
  39.         {
  40.             if (!dict.Contains(key)) return null;
  41.             ObjectId id = (ObjectId)dict[key];
  42.             if (id.ObjectClass != RXObject.GetClass(typeof(Xrecord))) return null;
  43.             return id.GetObject<Xrecord>().Data;
  44.         }
  45.  
  46.         // Sets the xrecord data
  47.         public static void SetXrecordData(this DBDictionary dict, string key, ResultBuffer data)
  48.         {
  49.             Xrecord xrec;
  50.             if (dict.Contains(key))
  51.             {
  52.                 xrec = ((ObjectId)dict[key]).GetObject<Xrecord>(OpenMode.ForWrite);
  53.             }
  54.             else
  55.             {
  56.                 dict.UpgradeOpen();
  57.                 xrec = new Xrecord();
  58.                 dict.SetAt(key, xrec);
  59.                 dict.Database.TransactionManager.TopTransaction.AddNewlyCreatedDBObject(xrec, true);
  60.             }
  61.             xrec.Data = data;
  62.         }
  63.  
  64.         // Sets the symbol table record unpurgeable by referencing it in its symbol table extension dictionary
  65.         public static void SetUnpurgeable(this SymbolTableRecord str)
  66.         {
  67.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  68.             DBDictionary xdict = st.GetOrCreateExtensionDictionary();
  69.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE") ?? new ResultBuffer();
  70.             data.Add(new TypedValue(340, str.ObjectId));
  71.             xdict.SetXrecordData("GILE_UNPURGE", data);
  72.         }
  73.  
  74.         // Resets the symbol table record purgeable by unreferencing it in its symbol table extension dictionary
  75.         public static void ResetPurgeable(this SymbolTableRecord str)
  76.         {
  77.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  78.             DBDictionary xdict = st.GetExtensionDictionary();
  79.             if (xdict == null) return;
  80.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE");
  81.             if (data == null) return;
  82.             var values = data.AsArray()
  83.                 .Where(tv => tv.TypeCode != 340 || (ObjectId)tv.Value != st.ObjectId)
  84.                 .ToArray();
  85.             xdict.SetXrecordData("GILE_UNPURGE", new ResultBuffer(values));
  86.         }
  87.     }

Using example, adds a new layer named "unpurgeable" to the layer table and set it 'unpurgeable':

Code - C#: [Select]
  1.             using (Transaction tr = db.TransactionManager.StartTransaction())
  2.             {
  3.                 LayerTable lt = db.LayerTableId.GetObject<LayerTable>(OpenMode.ForWrite);
  4.                 LayerTableRecord ltr = new LayerTableRecord();
  5.                 ltr.Name = "unpurgeable";
  6.                 lt.Add(ltr);
  7.                 tr.AddNewlyCreatedDBObject(ltr, true);
  8.                 ltr.SetUnpurgeable();
  9.                 tr.Commit();
  10.             }
Speaking English as a French Frog

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #2 on: April 22, 2016, 06:47:18 AM »
Sorry for my lack of knowledge on this but do i combine these two together & add my specific layers in. There's roughly 300 of them. Also how would i implement this? Do i need to compile this & turn it into a dll file or something?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #3 on: April 22, 2016, 07:01:58 AM »
Extension methods are used to add methods to a predefined type (see this page).

If you add the upper "Extensions" class to your project, those method will be available for the extended type (for your purpose, the SetUnpurgeable() method extents the SymbolTableRecord abstract type which LayerTableRecord derived from).
So, in you code, open each LayerTableRecord instance you want to set unpurgeable and call the SetUnpurgeable method on it.
Speaking English as a French Frog

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Using Hard pointers to manage layers
« Reply #4 on: April 22, 2016, 07:40:38 AM »
Gile, why wrap ObjectId.GetObject() with a GetObject method?  I think it was Tony T that pointed out the inefficiencies of ObjectId.GetObject().  Just asking, since I know you'll have a good reason.
Revit 2019, AMEP 2019 64bit Win 10

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #5 on: April 22, 2016, 08:41:16 AM »
Hi,

This is an old (short) version.

Here's the one I use now:

Code - C#: [Select]
  1.         public static T GetObject<T>(
  2.             this ObjectId id,
  3.             OpenMode mode = OpenMode.ForRead,
  4.             bool openErased = false,
  5.             bool forceOpenOnLockedLayer = false)
  6.             where T : DBObject
  7.         {
  8.             if (id == ObjectId.Null)
  9.                 throw new ArgumentException("The value cannot be ObjectId.Null.", "id");
  10.  
  11.             Transaction tr = id.Database.TransactionManager.TopTransaction;
  12.             if (tr == null)
  13.                 throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
  14.  
  15.             return (T)tr.GetObject(id, mode, openErased, forceOpenOnLockedLayer);
  16.         }

Anyway, even if ObjectId.GetObject() is (a little) less efficient than Transaction.GetOject() (which is less efficient than OpenCloseTransaction.GetObject() or ObjectId.Open()), the goal here was more to get a succint and readable code.
« Last Edit: April 23, 2016, 02:01:51 AM by gile »
Speaking English as a French Frog

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Using Hard pointers to manage layers
« Reply #6 on: April 22, 2016, 01:21:26 PM »
Quote
Anyway, even if ObjectId.GetObject() is (a little) less efficient than Transaction.GetOject() (which is less efficient than OpenCloseTransaction.GetObject() or ObjectId.Open()), the goal here was more to get a succint and readable code.

Just to be clear, I wasn't picking holes in your code.  I understand you were just showing the OP an example.  I thought maybe you knew something I didn't, since your knowledge of the API is better than mine.  Thank you for clarifying though.
Revit 2019, AMEP 2019 64bit Win 10

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #7 on: April 22, 2016, 01:48:50 PM »
Quote
since your knowledge of the API is better than mine.
I'm thinking the same about you.
Speaking English as a French Frog

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #8 on: April 22, 2016, 04:44:31 PM »
Extension methods are used to add methods to a predefined type (see this page).

If you add the upper "Extensions" class to your project, those method will be available for the extended type (for your purpose, the SetUnpurgeable() method extents the SymbolTableRecord abstract type which LayerTableRecord derived from).
So, in you code, open each LayerTableRecord instance you want to set unpurgeable and call the SetUnpurgeable method on it.
Sorry i'm such a dummy on this but if i understand you correctly i need to add all my layers into this code like layer1,layer2,layer3,etc. Also i am not sure how to implement this. Do i compile this into a dll file? This is my template file. i want all of these layers to be unpurgeable. Do i need to run this code in side of this file?

mjfarrell

  • Seagull
  • Posts: 14444
  • Every Student their own Lesson
Re: Using Hard pointers to manage layers
« Reply #9 on: April 22, 2016, 05:01:41 PM »
might it not be easier to tell people to STOP purging layers?

and or

frustrate the bugger....place a single autocad point on those layers...then he can't purge them...pdmode=1

they wont even be able to see them.... :whistling:
Be your Best


Michael Farrell
http://primeservicesglobal.com/

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #10 on: April 22, 2016, 06:13:18 PM »
sovby,

Yes, you can compile the extension class in a dll, this is a good way to build a re-usable library, but in this case it will be a very small library...

You can also, as I said, just add this class to the project which use it, even in a separate file or in the same file as the command method which use it as shown in the following example.

Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System.Linq;
  6. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. [assembly: CommandClass(typeof(SetAllLayersUnpurgeable.Commands))]
  9.  
  10. namespace SetAllLayersUnpurgeable
  11. {
  12.     public class Commands
  13.     {
  14.         [CommandMethod("Cmd")]
  15.         public void SetAllLayersUnpurgeableCmd()
  16.         {
  17.             var db = HostApplicationServices.WorkingDatabase;
  18.             using (var tr = db.TransactionManager.StartTransaction())
  19.             {
  20.                 var layerTable = db.LayerTableId.GetObject<LayerTable>();
  21.                 foreach (ObjectId layerId in layerTable)
  22.                 {
  23.                     if (layerId != db.LayerZero)
  24.                     {
  25.                         layerId.GetObject<LayerTableRecord>().SetUnpurgeable();
  26.                     }
  27.                 }
  28.                 tr.Commit();
  29.             }
  30.         }
  31.     }
  32.  
  33.     public static class Extensions
  34.     {
  35.         // Opens the object for read.
  36.         public static T GetObject<T>(this ObjectId id) where T : DBObject
  37.         {
  38.             return (T)id.GetObject(OpenMode.ForRead, false, false);
  39.         }
  40.  
  41.         // Opens the object in the specified mode
  42.         public static T GetObject<T>(this ObjectId id, OpenMode mode) where T : DBObject
  43.         {
  44.             return (T)id.GetObject(mode, false, false);
  45.         }
  46.  
  47.         // Gets the object extension dictionary (returns null if not found)
  48.         public static DBDictionary GetExtensionDictionary(this DBObject obj)
  49.         {
  50.             ObjectId dictId = obj.ExtensionDictionary;
  51.             if (dictId == ObjectId.Null)
  52.             {
  53.                 return null;
  54.             }
  55.             return dictId.GetObject<DBDictionary>();
  56.         }
  57.  
  58.         // Gets the object extension dictionary (creates it if not already exists)
  59.         public static DBDictionary GetOrCreateExtensionDictionary(this DBObject obj)
  60.         {
  61.             if (obj.ExtensionDictionary == ObjectId.Null)
  62.             {
  63.                 obj.UpgradeOpen();
  64.                 obj.CreateExtensionDictionary();
  65.             }
  66.             return obj.ExtensionDictionary.GetObject<DBDictionary>();
  67.         }
  68.  
  69.         // Gets the xrecord data
  70.         public static ResultBuffer GetXrecordData(this DBDictionary dict, string key)
  71.         {
  72.             if (!dict.Contains(key)) return null;
  73.             ObjectId id = (ObjectId)dict[key];
  74.             if (id.ObjectClass != RXObject.GetClass(typeof(Xrecord))) return null;
  75.             return id.GetObject<Xrecord>().Data;
  76.         }
  77.  
  78.         // Sets the xrecord data
  79.         public static void SetXrecordData(this DBDictionary dict, string key, ResultBuffer data)
  80.         {
  81.             Xrecord xrec;
  82.             if (dict.Contains(key))
  83.             {
  84.                 xrec = ((ObjectId)dict[key]).GetObject<Xrecord>(OpenMode.ForWrite);
  85.             }
  86.             else
  87.             {
  88.                 dict.UpgradeOpen();
  89.                 xrec = new Xrecord();
  90.                 dict.SetAt(key, xrec);
  91.                 dict.Database.TransactionManager.TopTransaction.AddNewlyCreatedDBObject(xrec, true);
  92.             }
  93.             xrec.Data = data;
  94.         }
  95.  
  96.         // Sets the symbol table record unpurgeable by referencing it in its symbol table extension dictionary
  97.         public static void SetUnpurgeable(this SymbolTableRecord str)
  98.         {
  99.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  100.             DBDictionary xdict = st.GetOrCreateExtensionDictionary();
  101.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE") ?? new ResultBuffer();
  102.             data.Add(new TypedValue(340, str.ObjectId));
  103.             xdict.SetXrecordData("GILE_UNPURGE", data);
  104.         }
  105.  
  106.         // Resets the symbol table record purgeable by unreferencing it in its symbol table extension dictionary
  107.         public static void ResetPurgeable(this SymbolTableRecord str)
  108.         {
  109.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  110.             DBDictionary xdict = st.GetExtensionDictionary();
  111.             if (xdict == null) return;
  112.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE");
  113.             if (data == null) return;
  114.             var values = data.AsArray()
  115.                 .Where(tv => tv.TypeCode != 340 || (ObjectId)tv.Value != st.ObjectId)
  116.                 .ToArray();
  117.             xdict.SetXrecordData("GILE_UNPURGE", new ResultBuffer(values));
  118.         }
  119.     }
  120. }
  121.  

mjfarrell,
Quote
frustrate the bugger....place a single autocad point on those layers...then he can't purge them...pdmode=1
This is what sovby did in the attached file. Anyone can set PDMODE to 3 (or whatever else to display the points), erase the points (within a block reference here) and purge all.
Speaking English as a French Frog

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: Using Hard pointers to manage layers
« Reply #11 on: April 22, 2016, 06:20:33 PM »
Well yeah, its always easier to tell them the right way.  They bob their head and reply "Yes.  Yes.  Yes." and promptly go back to doing what they think is right.

There can be other, non-user related complications.  We have some tools from one client which automatically purges everything every time a drawing is opened.
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

Jeff H

  • Needs a day job
  • Posts: 6144
Re: Using Hard pointers to manage layers
« Reply #12 on: April 23, 2016, 12:46:40 AM »

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #13 on: April 23, 2016, 01:37:29 PM »
might it not be easier to tell people to STOP purging layers?

and or

frustrate the bugger....place a single autocad point on those layers...then he can't purge them...pdmode=1

they wont even be able to see them.... :whistling:
Yeah it would be but in my case i don't think it would work. My solution was to create a block with points on every layer which i made small & put it in the bottom left corner but they found it & deleted the block. I guess i didn't hide it well enough.

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #14 on: April 23, 2016, 01:55:10 PM »
sovby,

Yes, you can compile the extension class in a dll, this is a good way to build a re-usable library, but in this case it will be a very small library...

You can also, as I said, just add this class to the project which use it, even in a separate file or in the same file as the command method which use it as shown in the following example.

Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System.Linq;
  6. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. [assembly: CommandClass(typeof(SetAllLayersUnpurgeable.Commands))]
  9.  
  10. namespace SetAllLayersUnpurgeable
  11. {
  12.     public class Commands
  13.     {
  14.         [CommandMethod("Cmd")]
  15.         public void SetAllLayersUnpurgeableCmd()
  16.         {
  17.             var db = HostApplicationServices.WorkingDatabase;
  18.             using (var tr = db.TransactionManager.StartTransaction())
  19.             {
  20.                 var layerTable = db.LayerTableId.GetObject<LayerTable>();
  21.                 foreach (ObjectId layerId in layerTable)
  22.                 {
  23.                     if (layerId != db.LayerZero)
  24.                     {
  25.                         layerId.GetObject<LayerTableRecord>().SetUnpurgeable();
  26.                     }
  27.                 }
  28.                 tr.Commit();
  29.             }
  30.         }
  31.     }
  32.  
  33.     public static class Extensions
  34.     {
  35.         // Opens the object for read.
  36.         public static T GetObject<T>(this ObjectId id) where T : DBObject
  37.         {
  38.             return (T)id.GetObject(OpenMode.ForRead, false, false);
  39.         }
  40.  
  41.         // Opens the object in the specified mode
  42.         public static T GetObject<T>(this ObjectId id, OpenMode mode) where T : DBObject
  43.         {
  44.             return (T)id.GetObject(mode, false, false);
  45.         }
  46.  
  47.         // Gets the object extension dictionary (returns null if not found)
  48.         public static DBDictionary GetExtensionDictionary(this DBObject obj)
  49.         {
  50.             ObjectId dictId = obj.ExtensionDictionary;
  51.             if (dictId == ObjectId.Null)
  52.             {
  53.                 return null;
  54.             }
  55.             return dictId.GetObject<DBDictionary>();
  56.         }
  57.  
  58.         // Gets the object extension dictionary (creates it if not already exists)
  59.         public static DBDictionary GetOrCreateExtensionDictionary(this DBObject obj)
  60.         {
  61.             if (obj.ExtensionDictionary == ObjectId.Null)
  62.             {
  63.                 obj.UpgradeOpen();
  64.                 obj.CreateExtensionDictionary();
  65.             }
  66.             return obj.ExtensionDictionary.GetObject<DBDictionary>();
  67.         }
  68.  
  69.         // Gets the xrecord data
  70.         public static ResultBuffer GetXrecordData(this DBDictionary dict, string key)
  71.         {
  72.             if (!dict.Contains(key)) return null;
  73.             ObjectId id = (ObjectId)dict[key];
  74.             if (id.ObjectClass != RXObject.GetClass(typeof(Xrecord))) return null;
  75.             return id.GetObject<Xrecord>().Data;
  76.         }
  77.  
  78.         // Sets the xrecord data
  79.         public static void SetXrecordData(this DBDictionary dict, string key, ResultBuffer data)
  80.         {
  81.             Xrecord xrec;
  82.             if (dict.Contains(key))
  83.             {
  84.                 xrec = ((ObjectId)dict[key]).GetObject<Xrecord>(OpenMode.ForWrite);
  85.             }
  86.             else
  87.             {
  88.                 dict.UpgradeOpen();
  89.                 xrec = new Xrecord();
  90.                 dict.SetAt(key, xrec);
  91.                 dict.Database.TransactionManager.TopTransaction.AddNewlyCreatedDBObject(xrec, true);
  92.             }
  93.             xrec.Data = data;
  94.         }
  95.  
  96.         // Sets the symbol table record unpurgeable by referencing it in its symbol table extension dictionary
  97.         public static void SetUnpurgeable(this SymbolTableRecord str)
  98.         {
  99.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  100.             DBDictionary xdict = st.GetOrCreateExtensionDictionary();
  101.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE") ?? new ResultBuffer();
  102.             data.Add(new TypedValue(340, str.ObjectId));
  103.             xdict.SetXrecordData("GILE_UNPURGE", data);
  104.         }
  105.  
  106.         // Resets the symbol table record purgeable by unreferencing it in its symbol table extension dictionary
  107.         public static void ResetPurgeable(this SymbolTableRecord str)
  108.         {
  109.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  110.             DBDictionary xdict = st.GetExtensionDictionary();
  111.             if (xdict == null) return;
  112.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE");
  113.             if (data == null) return;
  114.             var values = data.AsArray()
  115.                 .Where(tv => tv.TypeCode != 340 || (ObjectId)tv.Value != st.ObjectId)
  116.                 .ToArray();
  117.             xdict.SetXrecordData("GILE_UNPURGE", new ResultBuffer(values));
  118.         }
  119.     }
  120. }
  121.  

mjfarrell,
Quote
frustrate the bugger....place a single autocad point on those layers...then he can't purge them...pdmode=1
This is what sovby did in the attached file. Anyone can set PDMODE to 3 (or whatever else to display the points), erase the points (within a block reference here) and purge all.
I really apologize for asking this but i feel like i might need step by step directions for this. I have visual studio so would i use that to copy this code into or just notepad? What kind of file would i save it to? How do i get it to talk with Autocad? Again, i am very sorry but i have never done any thing like this before with Autocad. I have gotten a little bit more familiar with c# language but i am lost as to what the procedure would be to make this happen. Does this look at the file that i posted to get the layers or do i have to add them all in manually into this code? If so where exactly do i need to do this? You said to add the class to the projects that use it but i dont know what you mean. How do i do this?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #15 on: April 23, 2016, 02:20:20 PM »
Sorry, I really thaught you wanted some help with AutoCAD .NET programming, but it looks like you do not not know anything about AutoCAD .NET programming.
If you were looking for a built program, you should have say it in the OP.
I do not have time to spend learning you the basics of AutoCAD .NET programming, maybe someone else ?
All this can be done with AutoLISP which is must easier to run within AutoCAD, maybe you should start a thread in the AutoLISP forum.
Speaking English as a French Frog

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #16 on: April 23, 2016, 03:53:07 PM »
Sorry to bother you. In my defense i did say in my op that i didn't have much experience dealing with xrecords. Maybe i should have stated out right in the beginning that i'm a total newbie when it comes to .net programming. I did not think you could do something like this with lisp that's why i posted here. I will look elsewhere then & i will have to read up on some Autocad .net programming i guess. I have been looking at .net programming like c# so i should be able to pick up at least some of the concepts. It's just that i have not attempted something like this before so i was looking for some direction & i wasn't even sure if this could be done or not. The last thing i want to do is make a bunch of people with experience & knowledge of these kind of things angry at me.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8658
  • AKA Daniel
Re: Using Hard pointers to manage layers
« Reply #17 on: April 24, 2016, 12:16:49 AM »
i'm a total newbie when it comes to .net programming.

me too  :mrgreen:

Atook

  • Swamp Rat
  • Posts: 1027
  • AKA Tim
Re: Using Hard pointers to manage layers
« Reply #18 on: April 24, 2016, 01:49:33 AM »
..In my defense..

No need to defend yourself man. It was a simple question, we're all learning here.

It looks like you can accomplish what you want via LISP code, which will be easier for you to pull off than learning the .NET way of doing things. I'd follow Gile's advice, he's got a great handle on how to accomplish things in the realm of CAD coding.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #19 on: April 24, 2016, 03:47:00 AM »
@sovby

Assuming you know how to load and run AutoLISP code, here's a little LISP routine.

Code - Auto/Visual Lisp: [Select]
  1. ;; gc:GetExtDict (gile)
  2. ;; Retourne le dictionnaire d'extension de l'entité (ou nil)
  3. ;;
  4. ;; Argument : ent (ENAME)
  5. (defun gc:GetExtDict (ent)
  6.   (cdadr (member '(102 . "{ACAD_XDICTIONARY") (entget ent)))
  7. )
  8.  
  9. ;; gc:GetOrCreateExtDict (gile)
  10. ;; Retourne le dictionnaire d'extension de l'entité
  11. ;; Le dictionnaire est créé s'il n'existe pas
  12. ;;
  13. ;; Argument : ent (ENAME)
  14. (defun gc:GetOrCreateExtDict (ent / dict)
  15.   (cond
  16.     ((cdadr (member '(102 . "{ACAD_XDICTIONARY") (entget ent))))
  17.     ((setq dict (entmakex
  18.                   '((0 . "DICTIONARY") (100 . "AcDbDictionary"))
  19.                 )
  20.      )
  21.      (entmod (append (entget ent)
  22.                      (list '(102 . "{ACAD_XDICTIONARY")
  23.                            (cons 360 dict)
  24.                            '(102 . "}")
  25.                      )
  26.              )
  27.      )
  28.      dict
  29.     )
  30.   )
  31. )
  32.  
  33. ;; gc:SetXrecData
  34. ;; Retourne le ENAME du xrecord auquel sont affectées mes données
  35. ;;
  36. ;; Arguments
  37. ;; dict : ENAME du dictionnaire parent
  38. ;; key : nom du Xrecord
  39. ;; data : liste de paires pointées contenant les données
  40. (defun gc:SetXrecData (dict key data / xrec)
  41.   (if (snvalid key)
  42.     (progn
  43.       (and (setq xrec (dictsearch dict key))
  44.            (entdel (cdr (assoc -1 xrec)))
  45.       )
  46.       (dictadd
  47.         dict
  48.         key
  49.         (entmakex
  50.           (append
  51.             (list '(0 . "XRECORD")
  52.                   '(100 . "AcDbXrecord")
  53.             )
  54.             data
  55.           )
  56.         )
  57.       )
  58.     )
  59.   )
  60. )
  61.  
  62. ;;----------------------------------------------------;;
  63. ;;                      Commands                      ;;
  64. ;;----------------------------------------------------;;
  65.  
  66. ;; SETALLLAYERSUNPURGEABLE command to set all layers unpurgeable
  67. (defun c:SetAllLayersUnpurgeable (/ lay name ptrs tbl)
  68.   (while (setq lay (tblnext "LAYER" (not lay)))
  69.     (if (/= (setq name (cdr (assoc 2 lay))) "0")
  70.       (setq ptrs (cons (cons 340 (tblobjname "LAYER" name)) ptrs))
  71.     )
  72.   )
  73.   (setq tbl (cdr (assoc 330 (entget (tblobjname "LAYER" "0")))))
  74.   (gc:SetXrecData
  75.     (gc:GetOrCreateExtDict tbl)
  76.     "LAYER_UNPURGE"
  77.     ptrs
  78.   )
  79.   (princ)
  80. )
  81.  
  82. ;; RESETALLLAYERSPURGEABLE command to reset all layers purgeable
  83. (defun c:ResetAllLayersPurgeable (/ tbl xdict)
  84.   (setq tbl (cdr (assoc 330 (entget (tblobjname "LAYER" "0")))))
  85.   (and
  86.     (setq xdict (gc:GetExtDict tbl))
  87.     (dictremove xdict "LAYER_UNPURGE")
  88.   )
  89.   (princ)
  90. )
« Last Edit: April 24, 2016, 03:55:05 AM by gile »
Speaking English as a French Frog

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #20 on: April 24, 2016, 05:25:04 AM »
Yeah thanks i am familiar with lisp programs. I do still want to learn .Net though as it seems like it could do some stuff that lisp will not. Plus it's nice to learn for things not related to Autocad. It is nice to have something that hopefully will take care of the immediate problem. I was going through some tutorials that said to run the Autocad .Net wizard but on the Autodesk Developer website it lists the 2015 wizard as being for Visual Studio 2012. When i tried to install it i would of sworn it told me that i needed express 2013. The website lists Autocad .Net wizard 2017 as being for Visual Studio 2015. I have Visual Studio Community Edition 2015 both at home & at my office. I have Autocad 2015 installed at work but only Autocad 2014 at home. The website doesn't have anything listed for Autocad 2014 on windows. I guess it's not supported anymore?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #21 on: April 24, 2016, 07:03:44 AM »
Hi,

You can use VS 2015 to build AutoCAD .NET applications for any AutoCAD version supporting .NET (i.e. 2006 to 2017) but you probably can't get/use wizzards for all versions.

Personally, like many others, I recommend that you start by learning .NET (C #, OOP, Visual Studio, ...) without any relationship with AutoCAD. And only once the basics of .NET programming will be acquired, start learning the .NET API. At that time, you will see that you will not need (or want) to use wizzards and you should be able to start a new AutoCAD project from scratch and/or use your own project templates.
Speaking English as a French Frog

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #22 on: April 24, 2016, 09:43:32 AM »
Hi,

You can use VS 2015 to build AutoCAD .NET applications for any AutoCAD version supporting .NET (i.e. 2006 to 2017) but you probably can't get/use wizzards for all versions.

Personally, like many others, I recommend that you start by learning .NET (C #, OOP, Visual Studio, ...) without any relationship with AutoCAD. And only once the basics of .NET programming will be acquired, start learning the .NET API. At that time, you will see that you will not need (or want) to use wizzards and you should be able to start a new AutoCAD project from scratch and/or use your own project templates.
Ok thanks, i have actually been looking at C# using Visual tutorial for a little while so i do understand some of it but i have a long way to go. The only reason i was looking for the wizard is because that's what the tutorial from autodesk DevTV said to do.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #23 on: April 24, 2016, 10:10:08 AM »
When begining with AutoCAD .NET, you should start with AutoCAD 201X .NET Training lab which shows how to start an AutoCAD project from scratch.

If you can read French (or use an online translator) you can try to build your own AutoCAD .NET project templates following "C# Tempate" tutorial on this page.
« Last Edit: April 24, 2016, 11:16:08 AM by gile »
Speaking English as a French Frog

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #24 on: April 24, 2016, 11:54:37 AM »
When begining with AutoCAD .NET, you should start with AutoCAD 201X .NET Training lab which shows how to start an AutoCAD project from scratch.

If you can read French (or use an online translator) you can try to build your own AutoCAD .NET project templates following "C# Tempate" tutorial on this page.
Those are the tutorials i was watching from the Autodesk DEVTV youtube page. They tell you to install the wizards both the .NET wizard & the ObjectARX wizard. I don't speak french but i should be able to get started now although some of the videos are using an older version of visual Studio. Also i think the video was using visual basic versus the Lab word doc was using c#. Thanks for your help. I loaded your lisp routine & it worked perfectly. I wish i didnt have to even do this but not everyone pays attention to how things that they do effect other people

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Using Hard pointers to manage layers
« Reply #25 on: April 24, 2016, 12:30:25 PM »
There're simple exercices in the AutoCAD .NET Developer's Guide which show how strat a new AutoCAD project from scratch.
It seems these exercices don't exist for versions prior 2015 but it's quite easy to transpose.

Keep in mind that when Autodesk recommands which edition of Visual Studio to use with some AutoCAD version that means the earlier version of VS to be used (i.e. you can use Visual Studio 2015 with AutoCAD 2007 to 2017). Just care of the .NET Framework and AutoCAD libraries versions.

« Last Edit: April 24, 2016, 12:34:42 PM by gile »
Speaking English as a French Frog

sovby

  • Guest
Re: Using Hard pointers to manage layers
« Reply #26 on: April 24, 2016, 01:12:21 PM »
ok thanks for your help. It's a little hard to follow the tutorials from the Autodesk DEVTV youtube page that are talking about Visual Basic when i'm using c#. The syntax is slightly different. Luckily they give you finished code so that helps me figure out where to put the code from the lab word docs.

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
Re: Using Hard pointers to manage layers
« Reply #27 on: April 24, 2016, 01:54:24 PM »
If your goal is to protect a fixed list of layers, it is not really effective to start programming this.

You could use the trick to put a point on each layer. Personally I would place them on a Layout, it's harder to find. You could use an empty block which makes it even harder to find.

But if people do a Layer Merge, you lose them anyway. I think Layer Merge also will merge layers that have a Xrecord or are chained to a Xrecord.

If layers must exist, maybe it is better to provide tools to the users that will automatically use these layers. Make users happy to use the tools and then they will accept the layers too :-)
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

ChrisCarlson

  • Guest
Re: Using Hard pointers to manage layers
« Reply #28 on: April 25, 2016, 08:15:46 AM »
sovby,

Yes, you can compile the extension class in a dll, this is a good way to build a re-usable library, but in this case it will be a very small library...

You can also, as I said, just add this class to the project which use it, even in a separate file or in the same file as the command method which use it as shown in the following example.

Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System.Linq;
  6. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. [assembly: CommandClass(typeof(SetAllLayersUnpurgeable.Commands))]
  9.  
  10. namespace SetAllLayersUnpurgeable
  11. {
  12.     public class Commands
  13.     {
  14.         [CommandMethod("Cmd")]
  15.         public void SetAllLayersUnpurgeableCmd()
  16.         {
  17.             var db = HostApplicationServices.WorkingDatabase;
  18.             using (var tr = db.TransactionManager.StartTransaction())
  19.             {
  20.                 var layerTable = db.LayerTableId.GetObject<LayerTable>();
  21.                 foreach (ObjectId layerId in layerTable)
  22.                 {
  23.                     if (layerId != db.LayerZero)
  24.                     {
  25.                         layerId.GetObject<LayerTableRecord>().SetUnpurgeable();
  26.                     }
  27.                 }
  28.                 tr.Commit();
  29.             }
  30.         }
  31.     }
  32.  
  33.     public static class Extensions
  34.     {
  35.         // Opens the object for read.
  36.         public static T GetObject<T>(this ObjectId id) where T : DBObject
  37.         {
  38.             return (T)id.GetObject(OpenMode.ForRead, false, false);
  39.         }
  40.  
  41.         // Opens the object in the specified mode
  42.         public static T GetObject<T>(this ObjectId id, OpenMode mode) where T : DBObject
  43.         {
  44.             return (T)id.GetObject(mode, false, false);
  45.         }
  46.  
  47.         // Gets the object extension dictionary (returns null if not found)
  48.         public static DBDictionary GetExtensionDictionary(this DBObject obj)
  49.         {
  50.             ObjectId dictId = obj.ExtensionDictionary;
  51.             if (dictId == ObjectId.Null)
  52.             {
  53.                 return null;
  54.             }
  55.             return dictId.GetObject<DBDictionary>();
  56.         }
  57.  
  58.         // Gets the object extension dictionary (creates it if not already exists)
  59.         public static DBDictionary GetOrCreateExtensionDictionary(this DBObject obj)
  60.         {
  61.             if (obj.ExtensionDictionary == ObjectId.Null)
  62.             {
  63.                 obj.UpgradeOpen();
  64.                 obj.CreateExtensionDictionary();
  65.             }
  66.             return obj.ExtensionDictionary.GetObject<DBDictionary>();
  67.         }
  68.  
  69.         // Gets the xrecord data
  70.         public static ResultBuffer GetXrecordData(this DBDictionary dict, string key)
  71.         {
  72.             if (!dict.Contains(key)) return null;
  73.             ObjectId id = (ObjectId)dict[key];
  74.             if (id.ObjectClass != RXObject.GetClass(typeof(Xrecord))) return null;
  75.             return id.GetObject<Xrecord>().Data;
  76.         }
  77.  
  78.         // Sets the xrecord data
  79.         public static void SetXrecordData(this DBDictionary dict, string key, ResultBuffer data)
  80.         {
  81.             Xrecord xrec;
  82.             if (dict.Contains(key))
  83.             {
  84.                 xrec = ((ObjectId)dict[key]).GetObject<Xrecord>(OpenMode.ForWrite);
  85.             }
  86.             else
  87.             {
  88.                 dict.UpgradeOpen();
  89.                 xrec = new Xrecord();
  90.                 dict.SetAt(key, xrec);
  91.                 dict.Database.TransactionManager.TopTransaction.AddNewlyCreatedDBObject(xrec, true);
  92.             }
  93.             xrec.Data = data;
  94.         }
  95.  
  96.         // Sets the symbol table record unpurgeable by referencing it in its symbol table extension dictionary
  97.         public static void SetUnpurgeable(this SymbolTableRecord str)
  98.         {
  99.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  100.             DBDictionary xdict = st.GetOrCreateExtensionDictionary();
  101.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE") ?? new ResultBuffer();
  102.             data.Add(new TypedValue(340, str.ObjectId));
  103.             xdict.SetXrecordData("GILE_UNPURGE", data);
  104.         }
  105.  
  106.         // Resets the symbol table record purgeable by unreferencing it in its symbol table extension dictionary
  107.         public static void ResetPurgeable(this SymbolTableRecord str)
  108.         {
  109.             SymbolTable st = str.OwnerId.GetObject<SymbolTable>();
  110.             DBDictionary xdict = st.GetExtensionDictionary();
  111.             if (xdict == null) return;
  112.             ResultBuffer data = xdict.GetXrecordData("GILE_UNPURGE");
  113.             if (data == null) return;
  114.             var values = data.AsArray()
  115.                 .Where(tv => tv.TypeCode != 340 || (ObjectId)tv.Value != st.ObjectId)
  116.                 .ToArray();
  117.             xdict.SetXrecordData("GILE_UNPURGE", new ResultBuffer(values));
  118.         }
  119.     }
  120. }
  121.  

mjfarrell,
Quote
frustrate the bugger....place a single autocad point on those layers...then he can't purge them...pdmode=1
This is what sovby did in the attached file. Anyone can set PDMODE to 3 (or whatever else to display the points), erase the points (within a block reference here) and purge all.
I really apologize for asking this but i feel like i might need step by step directions for this. I have visual studio so would i use that to copy this code into or just notepad? What kind of file would i save it to? How do i get it to talk with Autocad? Again, i am very sorry but i have never done any thing like this before with Autocad. I have gotten a little bit more familiar with c# language but i am lost as to what the procedure would be to make this happen. Does this look at the file that i posted to get the layers or do i have to add them all in manually into this code? If so where exactly do i need to do this? You said to add the class to the projects that use it but i dont know what you mean. How do i do this?


I highly suggest you stop where you are, get the basics down on the c# language (writing, parts, compiling, etc) and then pick this back up. You really cannot write AutoCAD .NET without knowing the fundamentals.

lamarn

  • Swamp Rat
  • Posts: 636
Re: Using Hard pointers to manage layers
« Reply #29 on: July 07, 2016, 03:14:00 AM »
@sovby

Assuming you know how to load and run AutoLISP code, here's a little LISP routine.

Code - Auto/Visual Lisp: [Select]
  1. ;; gc:GetExtDict (gile)
  2. ;; Retourne le dictionnaire d'extension de l'entité (ou nil)
  3. ;;
  4. ;; Argument : ent (ENAME)
  5. (defun gc:GetExtDict (ent)
  6.   (cdadr (member '(102 . "{ACAD_XDICTIONARY") (entget ent)))
  7. )
  8.  
  9. ;; gc:GetOrCreateExtDict (gile)
  10. ;; Retourne le dictionnaire d'extension de l'entité
  11. ;; Le dictionnaire est créé s'il n'existe pas
  12. ;;
  13. ;; Argument : ent (ENAME)
  14. (defun gc:GetOrCreateExtDict (ent / dict)
  15.   (cond
  16.     ((cdadr (member '(102 . "{ACAD_XDICTIONARY") (entget ent))))
  17.     ((setq dict (entmakex
  18.                   '((0 . "DICTIONARY") (100 . "AcDbDictionary"))
  19.                 )
  20.      )
  21.      (entmod (append (entget ent)
  22.                      (list '(102 . "{ACAD_XDICTIONARY")
  23.                            (cons 360 dict)
  24.                            '(102 . "}")
  25.                      )
  26.              )
  27.      )
  28.      dict
  29.     )
  30.   )
  31. )
  32.  
  33. ;; gc:SetXrecData
  34. ;; Retourne le ENAME du xrecord auquel sont affectées mes données
  35. ;;
  36. ;; Arguments
  37. ;; dict : ENAME du dictionnaire parent
  38. ;; key : nom du Xrecord
  39. ;; data : liste de paires pointées contenant les données
  40. (defun gc:SetXrecData (dict key data / xrec)
  41.   (if (snvalid key)
  42.     (progn
  43.       (and (setq xrec (dictsearch dict key))
  44.            (entdel (cdr (assoc -1 xrec)))
  45.       )
  46.       (dictadd
  47.         dict
  48.         key
  49.         (entmakex
  50.           (append
  51.             (list '(0 . "XRECORD")
  52.                   '(100 . "AcDbXrecord")
  53.             )
  54.             data
  55.           )
  56.         )
  57.       )
  58.     )
  59.   )
  60. )
  61.  
  62. ;;----------------------------------------------------;;
  63. ;;                      Commands                      ;;
  64. ;;----------------------------------------------------;;
  65.  
  66. ;; SETALLLAYERSUNPURGEABLE command to set all layers unpurgeable
  67. (defun c:SetAllLayersUnpurgeable (/ lay name ptrs tbl)
  68.   (while (setq lay (tblnext "LAYER" (not lay)))
  69.     (if (/= (setq name (cdr (assoc 2 lay))) "0")
  70.       (setq ptrs (cons (cons 340 (tblobjname "LAYER" name)) ptrs))
  71.     )
  72.   )
  73.   (setq tbl (cdr (assoc 330 (entget (tblobjname "LAYER" "0")))))
  74.   (gc:SetXrecData
  75.     (gc:GetOrCreateExtDict tbl)
  76.     "LAYER_UNPURGE"
  77.     ptrs
  78.   )
  79.   (princ)
  80. )
  81.  
  82. ;; RESETALLLAYERSPURGEABLE command to reset all layers purgeable
  83. (defun c:ResetAllLayersPurgeable (/ tbl xdict)
  84.   (setq tbl (cdr (assoc 330 (entget (tblobjname "LAYER" "0")))))
  85.   (and
  86.     (setq xdict (gc:GetExtDict tbl))
  87.     (dictremove xdict "LAYER_UNPURGE")
  88.   )
  89.   (princ)
  90. )

I would like to turn this code to make it work for a certain selection of layers (my layers named 1-10).
Having a hard time figuring out how to get a list of layers using (tblobjname "layer" ..)
Any help would be a appriciated, thanks anyway
Design is something you should do with both hands. My 2d hand , my 3d hand ..

lamarn

  • Swamp Rat
  • Posts: 636
Re: Using Hard pointers to manage layers
« Reply #30 on: July 08, 2016, 10:05:35 AM »
Very happy with the code mr. Roy_43 provided
Works like a charm!
 
Using AutoCAD

Code: [Select]
(defun c:Unpurg (/ enm lst ptrs)
  (setq lst '("1" "2" "3")) ; Laagnamen.
  (foreach nme lst
    (if (setq enm (tblobjname "LAYER" nme))
      (setq ptrs (cons (cons 340 enm) ptrs))
    )
  )
  (gc:SetXrecData
    (gc:GetOrCreateExtDict (cdr (assoc 330 (entget (tblobjname "LAYER" "0")))))
    "LAYER_UNPURGE"
    ptrs
  )
  (princ)
)

Using BricsCAD
Code: [Select]
(defun gc:GetOrCreateExtDict (ent / dict)
  (cond
    ((cdadr (member '(102 . "{ACAD_XDICTIONARY") (entget ent))))
    ((setq dict (entmakex
                  '((0 . "DICTIONARY") (100 . "AcDbDictionary"))
                )
     )
     (entmod (append (list '(102 . "{ACAD_XDICTIONARY")
                           (cons 360 dict)
                           '(102 . "}")
                     )
                     (entget ent)
             )
     )
     dict
    )
  )
)
Design is something you should do with both hands. My 2d hand , my 3d hand ..