Author Topic: Object Events  (Read 12688 times)

0 Members and 1 Guest are viewing this topic.

BlackBox

  • King Gator
  • Posts: 3770
Object Events
« on: September 27, 2012, 01:06:39 PM »
After some much needed clarification on DocumentCollection Events in another thread, I thought I'd start learning more about Events in general in a similar way that I learned Visual LISP Reactors... So I am taking aspects of my LISP library that we use daily for production, and attempting to both port the code to C#, and enhance where possible.

Perhaps I am still just naive to the AutoCAD .NET API, but I was shocked when I looked into Autodesk.AutoCAD.DatabaseService.Viewport and found no Events at all... When I was hoping, anticipating, expecting to find some pseudo ViewportActivated, ViewportToBeActivated, ViewportDeactivated, ViewportToBeDeactivated, etc. Events.

Now, in my Visual LISP Reactor, I am relegated to using a callback to check for MSpace Object to detect when a PViewport is active.

To replicate this functionality (which works fine, again, I'm just trying to learn porting existing code) in C#, should I follow the same logic by registering (+=) a CommandEnded Event handler, etc.... or am I missing something?

TIA
"How we think determines what we do, and what we do determines what we get."

TheMaster

  • Guest
Re: Object Events
« Reply #1 on: September 28, 2012, 09:26:25 PM »
After some much needed clarification on DocumentCollection Events in another thread, I thought I'd start learning more about Events in general in a similar way that I learned Visual LISP Reactors... So I am taking aspects of my LISP library that we use daily for production, and attempting to both port the code to C#, and enhance where possible.

Perhaps I am still just naive to the AutoCAD .NET API, but I was shocked when I looked into Autodesk.AutoCAD.DatabaseService.Viewport and found no Events at all... When I was hoping, anticipating, expecting to find some pseudo ViewportActivated, ViewportToBeActivated, ViewportDeactivated, ViewportToBeDeactivated, etc. Events.

Now, in my Visual LISP Reactor, I am relegated to using a callback to check for MSpace Object to detect when a PViewport is active.

To replicate this functionality (which works fine, again, I'm just trying to learn porting existing code) in C#, should I follow the same logic by registering (+=) a CommandEnded Event handler, etc.... or am I missing something?

TIA

I think the CVPORT system variable will tell you when the active viewport has changed, that is assuming that the SystemVariableChanged event fires when it changes (it doesn't fire for all system variables).

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #2 on: September 29, 2012, 01:07:54 AM »
Thanks for the reply, Tony.

My existing VL code uses ActiveSpace Object in lieu of CVPORT, but I greatly appreciate the tip about the Variable Changed event.
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #3 on: October 05, 2012, 02:59:42 PM »
After testing a combination of both TILEMODE and CVPORT to account for three conditions (i.e., ModelSpace, PaperSpace, and PViewport Active), the SystemVariableChanged event misfires when switching from Model Tab to Layout* Tab, but does fire correctly when switching from PViewport Active to PaperSpace.

The behavior I am observing, is that when switching from Model Tab (CVPORT = 2), to the only Layout Tab in my test drawing (CVPORT = 1), the SystemVariableChanged Event fires as I switch to Layout Tab, CVPORT is queried with a value of 2 (Model Tab) prior to the tab actually having been switched.

My thought is to continue with my original VL coding logic (which was very successful over the past couple of years), by monitoring CommandEnded Event for:

Code - C#: [Select]
  1.         public static void Document_CommandEnded(Object sender, CommandEventArgs e)
  2.         {
  3.             if (Utils.WcMatch(e.GlobalCommandName.ToUpper(),
  4.                 "*CHSPACE,*MSPACE,*PSPACE,LAYOUT_CONTROL,U,*UNDO,*VPMAX,*VPMIN"))
  5.             {
  6.                // ...
  7.             }
  8.         }
  9.  

... But I still require an effective test expression for determining the Active Space.

I found an example that I believe can be modified to suite my needs, but I wondered if this may be a good place to employ StartOpenCloseTransaction()?

Obviously not much (if any) documentation on this, only going from others' posts on the topic to the best of my understanding.

Am I correct in that when using StartOpenCloseTransaction() that I need not use Commit()? Also, as this is going to be employed across all open Documents, is there any advise on using this method for testing Active Space that will allow me to avoid errors (not that I've experienced any yet, just new to .NET and want to do a bit of CYA)?

As I understand it, I'd only need to open BlockTableRecord ForRead to query the active space's Name Property, then end the Transaction... Perhaps something like:

Code - C#: [Select]
  1. // ...
  2.             Document doc = Application.DocumentManager.MdiActiveDocument;
  3.             Editor ed = doc.Editor;
  4.             Database db = doc.Database;
  5.  
  6.             Transaction tr =
  7.                 db.TransactionManager.StartOpenCloseTransaction();
  8.  
  9.             using (tr)
  10.             {
  11.                 BlockTable bt =
  12.                   (BlockTable)tr.GetObject(
  13.                     db.BlockTableId,
  14.                     OpenMode.ForRead
  15.                   );
  16.  
  17.                 BlockTableRecord btr =
  18.                   (BlockTableRecord)tr.GetObject(
  19.                     db.CurrentSpaceId,
  20.                     OpenMode.ForRead
  21.                   );
  22.  
  23.                 ed.WriteMessage("\n** " + btr.Name + " ** \n");
  24.             }
  25. // ...
  26.  

TIA

"How we think determines what we do, and what we do determines what we get."

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Object Events
« Reply #4 on: October 05, 2012, 11:23:57 PM »
After testing a combination of both TILEMODE and CVPORT to account for three conditions (i.e., ModelSpace, PaperSpace, and PViewport Active), the SystemVariableChanged event misfires when switching from Model Tab to Layout* Tab, but does fire correctly when switching from PViewport Active to PaperSpace.

The behavior I am observing, is that when switching from Model Tab (CVPORT = 2), to the only Layout Tab in my test drawing (CVPORT = 1), the SystemVariableChanged Event fires as I switch to Layout Tab, CVPORT is queried with a value of 2 (Model Tab) prior to the tab actually having been switched.

I not sure I follow exactly what your saying but it is very easy for me to be confused.
Are you saying when switching from model tab to a layout tab the SystemVariableChanged event is not firing at all or you saying it is not firing for CVPORT?
When switching tabs I think the SystemVariableChanged event should fire for TILEMODE.
Using MdbDbg event thingy TILEMODE is changed when switching between any tabs(although the value is same when switching between 2 paperspace tabs it still fires) looks like a VIEWBACKSTATUS changes also but have no idea what its doing.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Object Events
« Reply #5 on: October 05, 2012, 11:42:00 PM »
Turning Application events on in MgdDbg here is what I get
Also depending on how many viewports, viewport properties, system variables like LAYOUTREGENCTL, etc..... will probably cause different results.
 
 
switching from model to paper tab
Code: [Select]

 Command:   <Switching to: Layout1>
[App Event] : System Var Changing       : NAVBARDISPLAY
[App Event] : System Var Changed        : NAVBARDISPLAY
[App Event] : System Var Changing       : NAVBARDISPLAY
[App Event] : System Var Changed        : NAVBARDISPLAY
[App Event] : System Var Changing       : CLAYER
[App Event] : System Var Changed        : CLAYER
[App Event] : System Var Changing       : TILEMODE
[App Event] : System Var Changed        : TILEMODE
[App Event] : System Var Changing       : REGENMODE
[App Event] : System Var Changed        : REGENMODE
[App Event] : System Var Changing       : REGENMODE
[App Event] : System Var Changed        : REGENMODERestoring cached viewports - Regenerating layout.
[App Event] : System Var Changing       : VIEWBACKSTATUS
[App Event] : System Var Changed        : VIEWBACKSTATUS

 
Switching from paper to another paper
Code: [Select]

 Command:   <Switching to: Layout2>
[App Event] : System Var Changing       : VIEWBACKSTATUS
[App Event] : System Var Changed        : VIEWBACKSTATUS
[App Event] : System Var Changing       : NAVBARDISPLAY
[App Event] : System Var Changed        : NAVBARDISPLAY
[App Event] : System Var Changing       : NAVBARDISPLAY
[App Event] : System Var Changed        : NAVBARDISPLAY
[App Event] : System Var Changing       : CLAYER
[App Event] : System Var Changed        : CLAYER
[App Event] : System Var Changing       : UCSNAME
[App Event] : System Var Changed        : UCSNAME
[App Event] : System Var Changing       : PSLTSCALE
[App Event] : System Var Changed        : PSLTSCALE
[App Event] : System Var Changing       : PLIMCHECK
[App Event] : System Var Changed        : PLIMCHECK
[App Event] : System Var Changing       : PINSBASE
[App Event] : System Var Changed        : PINSBASE
[App Event] : System Var Changing       : PLIMMIN
[App Event] : System Var Changed        : PLIMMIN
[App Event] : System Var Changing       : PLIMMAX
[App Event] : System Var Changed        : PLIMMAX
[App Event] : System Var Changing       : PEXTMIN
[App Event] : System Var Changed        : PEXTMIN
[App Event] : System Var Changing       : PEXTMAX
[App Event] : System Var Changed        : PEXTMAX
[App Event] : System Var Changing       : TILEMODE
[App Event] : System Var Changed        : TILEMODE
[App Event] : System Var Changing       : REGENMODE
[App Event] : System Var Changed        : REGENMODE
[App Event] : System Var Changing       : REGENMODE
[App Event] : System Var Changed        : REGENMODERestoring cached viewports - Regenerating layout.
[App Event] : System Var Changing       : VIEWBACKSTATUS
[App Event] : System Var Changed        : VIEWBACKSTATUS

 
Switching from paper to model
Code: [Select]

Command:   <Switching to: Model>
[App Event] : System Var Changing       : VIEWBACKSTATUS
[App Event] : System Var Changed        : VIEWBACKSTATUS
[App Event] : System Var Changing       : NAVBARDISPLAY
[App Event] : System Var Changed        : NAVBARDISPLAY
[App Event] : System Var Changing       : NAVBARDISPLAY
[App Event] : System Var Changed        : NAVBARDISPLAY
[App Event] : System Var Changing       : CLAYER
[App Event] : System Var Changed        : CLAYER
[App Event] : System Var Changing       : UCSNAME
[App Event] : System Var Changed        : UCSNAME
[App Event] : System Var Changing       : TILEMODE
[App Event] : System Var Changed        : TILEMODERestoring cached viewports.
[App Event] : System Var Changing       : UCSNAME
[App Event] : System Var Changed        : UCSNAME

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #6 on: October 06, 2012, 04:16:24 AM »
Sorry for any confusion, Jeff.

Put simply, I'm trying to identify the active space... Model Tab, Layout Tab (PViewport inactive), Layout Tab (PViewport active).

My VL code uses a command reactor's CommandEnded Event to test for the WCMATCH string posted above, and qualifies the active space to perform one of three actions (one for each condition).

The SystemVariableChanged Event fires, it's just that Tony's warning holds true for CVPORT; perhaps an alternative to using the CommandEnded Event is to use a LayoutActivated Event (not tested, just read up on a bit today).

Also, thanks for educating me about MgdDbg... Never heard of that before... Found an ADN article & DevCast that goes over ArxDbg, MgdDbg, and Inspector.

Cheers! :beer:
"How we think determines what we do, and what we do determines what we get."

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Object Events
« Reply #7 on: October 06, 2012, 04:48:22 AM »
MgdDbg is a must have.
 
 
Do you have to know when they change or just need to know what action to take in a method depending on which space is active?
 
Also if your inside a viewport or the viewport is active then Database.CurrentSpaceId will return ModelSpaces ObjectID.
 
 
 
 

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #8 on: October 06, 2012, 01:28:33 PM »
MgdDbg is a must have.

Just downloaded them (ArxDbg, MgdDbg, and Inspector) from the DevCast article mentioned above.
 
Do you have to know when they change or just need to know what action to take in a method depending on which space is active?
 
Also if your inside a viewport or the viewport is active then Database.CurrentSpaceId will return ModelSpaces ObjectID.

All of what follows was done prior to knowing of MgdDbg, so take from this what you will:



The initial attempt was to monitor when CVPORT changed to determine which space was active, which is not successful in all three conditions.

Sample command line output:
Code - Auto/Visual Lisp: [Select]
  1. Command: netload Assembly file name: "FOO.dll"
  2. FOO events registered
  3.  
  4. ** TILEMODE: 1 **
  5.  
  6. ** ModelSpace activated **
  7.  
  8. Command: _RIBBON
  9.  
  10. Command: COMMANDLINE
  11.  
  12. Command: properties
  13.  
  14. Command: *Cancel*
  15.  
  16. Command: *Cancel*
  17.  
  18. Command:   <Switching to: Layout1>
  19.  
  20. ** TILEMODE: 0 **
  21.  
  22. ** CVPORT: 2 **
  23. Regenerating layout.
  24.  
  25. Command: mview
  26.  
  27. Specify corner of viewport or
  28. [ON/OFF/Fit/Shadeplot/Lock/Object/Polygonal/Restore/LAyer/2/3/4] <Fit>:
  29. Specify opposite corner: Regenerating model.
  30.  
  31. Command: Specify opposite corner: *Cancel*
  32.  
  33. Command: _.MSPACE
  34. ** TILEMODE: 0 **
  35.  
  36. ** CVPORT: 2 **
  37.  
  38. Command: _.PSPACE
  39. ** TILEMODE: 0 **
  40.  
  41. ** CVPORT: 1 **
  42.  
  43. ** PaperSpace activated **
  44.  
  45. Command: *Cancel*
  46.  
  47. Command: *Cancel*
  48.  
  49. Command:   <Switching to: Model>
  50.  
  51. ** TILEMODE: 1 **
  52.  
  53. ** ModelSpace activated **
  54. Regenerating model.
  55.  
  56. Command: *Cancel*
  57.  
  58. Command: *Cancel*
  59.  
  60. Command:   <Switching to: Layout1>
  61.  
  62. ** TILEMODE: 0 **
  63.  
  64. ** CVPORT: 2 **
  65. Regenerating layout.
  66. Regenerating model.
  67.  



My next thought was that I need to first test for TILEMODE / CTAB to determine Model, or Layout. Then, if Layout check the BlockTableRecord.Name to determine *Paper_Space (PViewport inactive), or *Model_Space (PViewport active).

Code - C#: [Select]
  1.  
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5.  
  6. using acApp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. using System;
  9.  
  10. namespace FOO
  11. {
  12.     public class Events
  13.     {
  14.         public static void Register()
  15.         {
  16.             acApp.SystemVariableChanged -= acApp_SystemVariableChanged;
  17.             acApp.SystemVariableChanged += acApp_SystemVariableChanged;
  18.  
  19.             DocumentCollection acDocs = acApp.DocumentManager;
  20.  
  21.             acDocs.DocumentActivated -= acDocs_DocumentActivated;
  22.             acDocs.DocumentActivated += acDocs_DocumentActivated;
  23.  
  24.             acDocs.MdiActiveDocument.Editor.WriteMessage(
  25.                 "\nFOO events registered \n"
  26.                 );
  27.  
  28.             Reconcile();
  29.         }
  30.  
  31.         public static void acApp_SystemVariableChanged(Object sender, SystemVariableChangedEventArgs e)
  32.         {
  33.             if (e.Changed == true)
  34.             {
  35.                 if (e.Name == "CVPORT" || e.Name == "TILEMODE")
  36.                 {
  37.                     Reconcile();
  38.                 }
  39.             }            
  40.         }
  41.  
  42.         public static void acDocs_DocumentActivated(Object sender, DocumentCollectionEventArgs e)
  43.         {
  44.             if (e.Document != null)
  45.             {
  46.                 Reconcile();
  47.             }
  48.         }
  49.  
  50.         public static bool _PViewportActive(Document doc)
  51.         {
  52.             Database db = doc.Database;
  53.  
  54.             Transaction tr =
  55.                 // Which one is better for this purpose?
  56.                 db.TransactionManager.StartOpenCloseTransaction();
  57.                 //db.TransactionManager.StartTransaction();
  58.  
  59.             using (tr)
  60.             {
  61.                 BlockTable bt =
  62.                   (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  63.  
  64.                 BlockTableRecord btr =
  65.                   (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
  66.  
  67.                 if (btr.Name.ToUpper() == "*MODEL_SPACE")
  68.                 {
  69.                     return true;
  70.                 }
  71.  
  72.                 else
  73.                 {
  74.                     return false;
  75.                 }
  76.             }
  77.         }
  78.  
  79.         public static void Reconcile()
  80.         {
  81.             DocumentCollection acDocs = acApp.DocumentManager;
  82.             Document doc = acDocs.MdiActiveDocument;
  83.             Editor ed = doc.Editor;
  84.  
  85.             string tilemode = acApp.GetSystemVariable("TILEMODE").ToString();
  86.  
  87.             // Model space
  88.             if (tilemode == "1")
  89.             {
  90.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  91.             }
  92.  
  93.             else
  94.             {
  95.                 // Pviewport
  96.                 if (_PViewportActive(doc))
  97.                 {
  98.                     ed.WriteMessage("\n** PViewport active ** \n");
  99.                 }
  100.  
  101.                 // Paper space
  102.                 else
  103.                 {
  104.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  105.                 }
  106.             }
  107.         }
  108.  
  109.     }
  110. }
  111.  

... But that seems to suffer the same issue, in that when switching from Model Tab to Layout Tab, BlockTableRecord.Name returns *Model_Space before the Tab has actually changed, resulting in detecting PViewport Active when actually in *Paper_Space.

Sample command line output:
Code - Auto/Visual Lisp: [Select]
  1. Command: netload Assembly file name: "FOO.dll"
  2. FOO events registered
  3.  
  4. ** ModelSpace active **
  5.  
  6. Command: _RIBBON
  7.  
  8. Command: COMMANDLINE
  9.  
  10. Command: properties
  11.  
  12. Command: *Cancel*
  13.  
  14. Command: *Cancel*
  15.  
  16. Command:   <Switching to: Layout1>
  17.  
  18. ** PViewport active **
  19. Regenerating layout.
  20.  
  21. Command: mview
  22.  
  23. Specify corner of viewport or
  24. [ON/OFF/Fit/Shadeplot/Lock/Object/Polygonal/Restore/LAyer/2/3/4] <Fit>:
  25. Specify opposite corner: Regenerating model.
  26.  
  27. Command: Specify opposite corner: *Cancel*
  28.  
  29. Command: _.MSPACE
  30. ** PViewport active **
  31.  
  32. Command: _.PSPACE
  33. ** PaperSpace active **
  34.  
  35. Command: *Cancel*
  36.  
  37. Command: *Cancel*
  38.  
  39. Command:   <Switching to: Model>
  40.  
  41. ** ModelSpace active **
  42. Regenerating model.
  43.  
  44. Command: *Cancel*
  45.  
  46. Command: *Cancel*
  47.  
  48. Command:   <Switching to: Layout1>
  49.  
  50. ** PViewport active **
  51. Regenerating layout.
  52. Regenerating model.
  53.  

At this point, I'm not sure which would yield greater success (as I have not yet tested)... To either go the CommandEnded route (which I know to have worked for LISP), or to look into the LayoutActivated Event, rather than using the SystemVariableChanged Event.
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #9 on: October 06, 2012, 01:35:48 PM »
MgdDbg is just fantastic... Thanks for bringing it up, Jeff!

I also turned on Document Events (for Commands, etc.), and LayoutManager Events, and it appears that the CommandEnded Event is what I am after:

Code - Auto/Visual Lisp: [Select]
  1. [Doc Event] : Command will Start        : LAYOUT_CONTROL <Switching to: Layout1>
  2.  
  3. [App Event] : System Var Changing       : NAVBARDISPLAY
  4. [App Event] : System Var Changed        : NAVBARDISPLAY
  5. [App Event] : System Var Changing       : NAVBARDISPLAY
  6. [App Event] : System Var Changed        : NAVBARDISPLAY
  7. [App Event] : System Var Changing       : CLAYER
  8. [App Event] : System Var Changed        : CLAYER
  9. [App Event] : System Var Changing       : TILEMODE
  10. [App Event] : System Var Changed        : TILEMODE
  11. [App Event] : System Var Changing       : REGENMODE
  12. [App Event] : System Var Changed        : REGENMODE
  13. [App Event] : System Var Changing       : REGENMODE
  14. [App Event] : System Var Changed        : REGENMODERegenerating layout.
  15. Regenerating model.
  16.  
  17. [Layout Manager Event] : Layout Switched
  18. [Doc Event] : Command Ended             : LAYOUT_CONTROL
  19. Command: Specify opposite corner:
  20. [App Event] : System Var Changing       : DYNMODE
  21. [App Event] : System Var Changed        : DYNMODE
  22. [App Event] : System Var Changing       : DYNMODE
  23. [App Event] : System Var Changed        : DYNMODE*Cancel*
  24.  
  25. Command: _.MSPACE
  26. [Doc Event] : Command will Start        : MSPACE
  27. [App Event] : System Var Changing       : CVPORT
  28. [App Event] : System Var Changed        : CVPORT
  29. [Doc Event] : Command Ended             : MSPACE
  30. [App Event] : System Var Changing       : UCSNAME
  31. [App Event] : System Var Changed        : UCSNAME
  32. Command: _.PSPACE
  33. [Doc Event] : Command will Start        : PSPACE
  34. [App Event] : System Var Changing       : CVPORT
  35. [App Event] : System Var Changed        : CVPORT
  36. [Doc Event] : Command Ended             : PSPACE
  37.  
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #10 on: October 06, 2012, 01:42:42 PM »
It also appears that I could use Database Events, as the PViewport Object is modified when switching from Paper Space to Model Space (PViewport active)... But methinks the CommandEnded Event is just simpler, so I'm going to test that route.

After all, it worked well for my original VL routine. I just wanted to explore theses alternative routes to make sure there was not a simpler, perhaps better way moving forward into .NET API.
« Last Edit: October 06, 2012, 02:44:53 PM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #11 on: October 06, 2012, 02:27:43 PM »
Still more to add before I've fully ported my VL routine to .NET, but as for this particular task... I've got it sorted now.  :-)



Here's the working source-code (inefficient as it may be):

Code - C#: [Select]
  1.  
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Internal;
  6. using Autodesk.AutoCAD.Runtime;
  7.  
  8. using acApp = Autodesk.AutoCAD.ApplicationServices.Application;
  9.  
  10. using System;
  11.  
  12. [assembly: ExtensionApplication(typeof(FOO.Events))]
  13.  
  14. namespace FOO
  15. {
  16.     public class Events : IExtensionApplication
  17.     {
  18.         void IExtensionApplication.Initialize()
  19.         {
  20.             Editor ed = acApp.DocumentManager.MdiActiveDocument.Editor;
  21.             ed.WriteMessage("\nFOO events registered \n");
  22.  
  23.             Register();
  24.         }
  25.  
  26.         void IExtensionApplication.Terminate()
  27.         {
  28.         }
  29.  
  30.         public static void Register()
  31.         {
  32.             DocumentCollection acDocs = acApp.DocumentManager;
  33.             Editor ed = acDocs.MdiActiveDocument.Editor;
  34.  
  35.             try
  36.             {
  37.                 foreach (Document doc in acDocs)
  38.                 {
  39.                     doc.CommandEnded -= doc_CommandEnded;
  40.                     doc.CommandEnded += doc_CommandEnded;
  41.                 }
  42.  
  43.                 acDocs.DocumentCreated -= acDocs_DocumentCreated;
  44.                 acDocs.DocumentCreated += acDocs_DocumentCreated;
  45.  
  46.                 acDocs.DocumentActivated -= acDocs_DocumentActivated;
  47.                 acDocs.DocumentActivated += acDocs_DocumentActivated;
  48.  
  49.                 Reconcile();
  50.             }
  51.  
  52.             catch (System.Exception ex)
  53.             {
  54.                 ed.WriteMessage("\n; error: " + ex.Message + " \n");
  55.             }
  56.         }
  57.  
  58.         public static void acDocs_DocumentActivated(Object sender, DocumentCollectionEventArgs e)
  59.         {
  60.             if (e.Document != null)
  61.             {
  62.                 Reconcile();
  63.             }
  64.         }
  65.  
  66.         public static void acDocs_DocumentCreated(Object sender, DocumentCollectionEventArgs e)
  67.         {
  68.             if (e.Document != null)
  69.             {
  70.                 Register();
  71.             }
  72.         }
  73.  
  74.         public static void doc_CommandEnded(Object sender, CommandEventArgs e)
  75.         {
  76.             if (Utils.WcMatch(e.GlobalCommandName.ToUpper(),
  77.                 "*CHSPACE,*MSPACE,*PSPACE,*LAYOUT_CONTROL,U,*UNDO,*VPMAX,*VPMIN"))
  78.             {
  79.                 Reconcile();
  80.             }
  81.         }
  82.  
  83.         public static bool PViewportActive(Document doc)
  84.         {
  85.             Editor ed = doc.Editor;
  86.             Database db = doc.Database;
  87.  
  88.             Transaction tr =
  89.                 db.TransactionManager.StartOpenCloseTransaction();
  90.                 //db.TransactionManager.StartTransaction();
  91.  
  92.             using (tr)
  93.             {
  94.                 BlockTable bt =
  95.                   (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  96.  
  97.                 BlockTableRecord btr =
  98.                   (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
  99.  
  100.                 if (btr.Name.ToUpper() == "*MODEL_SPACE")
  101.                 {
  102.                     return true;
  103.                 }
  104.  
  105.                 else
  106.                 {
  107.                     return false;
  108.                 }
  109.             }
  110.         }
  111.  
  112.         public static void Reconcile()
  113.         {
  114.             DocumentCollection acDocs = acApp.DocumentManager;
  115.             Document doc = acDocs.MdiActiveDocument;
  116.             Editor ed = doc.Editor;
  117.  
  118.             string tilemode = acApp.GetSystemVariable("TILEMODE").ToString();
  119.  
  120.             // Model space
  121.             if (tilemode == "1")
  122.             {
  123.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  124.             }
  125.  
  126.             else
  127.             {
  128.                 // Pviewport
  129.                 if (PViewportActive(doc))
  130.                 {
  131.                     ed.WriteMessage("\n** PViewport active ** \n");
  132.                 }
  133.  
  134.                 // Paper space
  135.                 else
  136.                 {
  137.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  138.                 }
  139.             }
  140.            
  141.         }
  142.     }
  143. }
  144.  



Separately, I'd still appreciate it if someone could clarify if I am using StartOpenCloseTransaction() correctly, or if there's something I should watch for / correct in this test expression.

TIA
"How we think determines what we do, and what we do determines what we get."

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Object Events
« Reply #12 on: October 06, 2012, 04:55:53 PM »
I do not know but first thang that came to mind was could shorten a little and not have to open blocktable and BlockTablerecord
 
Code - C#: [Select]
  1.  
  2.          public static void Reconcile()
  3.         {
  4.             DocumentCollection acDocs = acApp.DocumentManager;
  5.             Document doc = acDocs.MdiActiveDocument;
  6.             Editor ed = doc.Editor;
  7.             Database db = doc.Database;
  8.             // Model space
  9.             if (db.TileMode)
  10.             {
  11.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  12.             }
  13.             else
  14.             {
  15.                 // Pviewport
  16.                 if (SymbolUtilityServices.GetBlockModelSpaceId(db) == db.CurrentSpaceId)
  17.                 {
  18.                     ed.WriteMessage("\n** PViewport active ** \n");
  19.                 }
  20.                 // Paper space
  21.                 else
  22.                 {
  23.                    
  24.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  25.                 }
  26.             }
  27.         }
  28.  

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #13 on: October 06, 2012, 05:54:34 PM »
I do not know but first thang that came to mind was could shorten a little and not have to open blocktable and BlockTablerecord

Excellent feedback, Jeff.

Another .NET novice mistake rectified by using Database.Tilemode, rather than acApp.GetSystemVariable("TILEMODE")... I didn't even know you could query it as a Property.

This alternative must have been overlooked in the Set and Return System Variables Documentation:roll:

Also, in all of the searching I did, not one illustrated the use of SymbolUtilityServices.GetBlockModelSpaceId(), so thanks also for this little lesson.

Cheers! :beer:
"How we think determines what we do, and what we do determines what we get."

TheMaster

  • Guest
Re: Object Events
« Reply #14 on: October 07, 2012, 04:57:57 AM »
Still more to add before I've fully ported my VL routine to .NET, but as for this particular task... I've got it sorted now.  :-)



Here's the working source-code (inefficient as it may be):

Code - C#: [Select]
  1.  
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5. using Autodesk.AutoCAD.Internal;
  6. using Autodesk.AutoCAD.Runtime;
  7.  
  8. using acApp = Autodesk.AutoCAD.ApplicationServices.Application;
  9.  
  10. using System;
  11.  
  12. [assembly: ExtensionApplication(typeof(FOO.Events))]
  13.  
  14. namespace FOO
  15. {
  16.     public class Events : IExtensionApplication
  17.     {
  18.         void IExtensionApplication.Initialize()
  19.         {
  20.             Editor ed = acApp.DocumentManager.MdiActiveDocument.Editor;
  21.             ed.WriteMessage("\nFOO events registered \n");
  22.  
  23.             Register();
  24.         }
  25.  
  26.         void IExtensionApplication.Terminate()
  27.         {
  28.         }
  29.  
  30.         public static void Register()
  31.         {
  32.             DocumentCollection acDocs = acApp.DocumentManager;
  33.             Editor ed = acDocs.MdiActiveDocument.Editor;
  34.  
  35.             try
  36.             {
  37.                 foreach (Document doc in acDocs)
  38.                 {
  39.                     doc.CommandEnded -= doc_CommandEnded;
  40.                     doc.CommandEnded += doc_CommandEnded;
  41.                 }
  42.  
  43.                 acDocs.DocumentCreated -= acDocs_DocumentCreated;
  44.                 acDocs.DocumentCreated += acDocs_DocumentCreated;
  45.  
  46.                 acDocs.DocumentActivated -= acDocs_DocumentActivated;
  47.                 acDocs.DocumentActivated += acDocs_DocumentActivated;
  48.  
  49.                 Reconcile();
  50.             }
  51.  
  52.             catch (System.Exception ex)
  53.             {
  54.                 ed.WriteMessage("\n; error: " + ex.Message + " \n");
  55.             }
  56.         }
  57.  
  58.         public static void acDocs_DocumentActivated(Object sender, DocumentCollectionEventArgs e)
  59.         {
  60.             if (e.Document != null)
  61.             {
  62.                 Reconcile();
  63.             }
  64.         }
  65.  
  66.         public static void acDocs_DocumentCreated(Object sender, DocumentCollectionEventArgs e)
  67.         {
  68.             if (e.Document != null)
  69.             {
  70.                 Register();
  71.             }
  72.         }
  73.  
  74.         public static void doc_CommandEnded(Object sender, CommandEventArgs e)
  75.         {
  76.             if (Utils.WcMatch(e.GlobalCommandName.ToUpper(),
  77.                 "*CHSPACE,*MSPACE,*PSPACE,*LAYOUT_CONTROL,U,*UNDO,*VPMAX,*VPMIN"))
  78.             {
  79.                 Reconcile();
  80.             }
  81.         }
  82.  
  83.         public static bool PViewportActive(Document doc)
  84.         {
  85.             Editor ed = doc.Editor;
  86.             Database db = doc.Database;
  87.  
  88.             Transaction tr =
  89.                 db.TransactionManager.StartOpenCloseTransaction();
  90.                 //db.TransactionManager.StartTransaction();
  91.  
  92.             using (tr)
  93.             {
  94.                 BlockTable bt =
  95.                   (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  96.  
  97.                 BlockTableRecord btr =
  98.                   (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
  99.  
  100.                 if (btr.Name.ToUpper() == "*MODEL_SPACE")
  101.                 {
  102.                     return true;
  103.                 }
  104.  
  105.                 else
  106.                 {
  107.                     return false;
  108.                 }
  109.             }
  110.         }
  111.  
  112.         public static void Reconcile()
  113.         {
  114.             DocumentCollection acDocs = acApp.DocumentManager;
  115.             Document doc = acDocs.MdiActiveDocument;
  116.             Editor ed = doc.Editor;
  117.  
  118.             string tilemode = acApp.GetSystemVariable("TILEMODE").ToString();
  119.  
  120.             // Model space
  121.             if (tilemode == "1")
  122.             {
  123.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  124.             }
  125.  
  126.             else
  127.             {
  128.                 // Pviewport
  129.                 if (PViewportActive(doc))
  130.                 {
  131.                     ed.WriteMessage("\n** PViewport active ** \n");
  132.                 }
  133.  
  134.                 // Paper space
  135.                 else
  136.                 {
  137.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  138.                 }
  139.             }
  140.            
  141.         }
  142.     }
  143. }
  144.  



Separately, I'd still appreciate it if someone could clarify if I am using StartOpenCloseTransaction() correctly, or if there's something I should watch for / correct in this test expression.

TIA

Before we talk about StartOpenCloseTransaction(), (and sorry to have to point this out), that code has major problems (which is perfectly fine if you're just learning the API and language- no offense intended).

What is the idea behind that Register() method (which is called every time a document is opened), and the removing and then immediately adding back event handlers for all open documents and the documents collection?


BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #15 on: October 07, 2012, 05:29:52 AM »
I am just learning this API, and am here to learn good coding practices, so any and all constructive criticism is welcomed.

In my other thread the topic of having only one instance of the event handler(s) was discussed, and it was my understanding by all of the comments that when registering an event adding immediately after removing was not uncommon.

Sorry to not quote specific comments here, I'm on a mobile device at the moment.

** Edit to add - the only alternatives that I recall, were to store each Document to a list for those already having registered the desired events, or to add a pseudo Loaded Propery to a Class for same.

Regardless, I want any code to be as efficient as possible within reason given my inexperience with the API. I have no expectation that anyone would just offer me a re-write in is entirety, but perhaps if you could describe where I have gone wrong, I can attempt to revise the code myself?

TIA
« Last Edit: October 07, 2012, 05:34:44 AM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #16 on: October 07, 2012, 06:18:13 AM »
Tony,
Quote
the removing and then immediately adding back event handlers
This is a practice I use to insure an event handler is not registered more than once for example within a Documentactivated event handler :
Code - C#: [Select]
  1.         void docMan_DocumentActivated(object sender, DocumentCollectionEventArgs e)
  2.         {
  3.             e.Document.CommandEnded -= doc_CommandEnded;
  4.             e.Document.CommandEnded += doc_CommandEnded;
  5.         }
Let me know if you think it is not a good practice and which one one you think is a better one.

RenderMan,
IMO, using both Documentactivated and DocumentCreated handlers is redundant.
If you used DocumentCreated, you do not need to remove the handler before adding it as the handler sould be add only once for a newly opened or created document.
If i do not misunderstand what you are trying to do, you want to register a CommandEnded event handler to all already opened documents when your application is loaded and to all those which may be created after.
Here's a way:
Code - C#: [Select]
  1.     public class Events : IExtensionApplication
  2.     {
  3.         private DocumentCollection acDocs;
  4.  
  5.         public void Initialize()
  6.         {
  7.             acDocs = acApp.DocumentManager;
  8.  
  9.             // register doc_CommandEnded for all already opened documents
  10.             foreach (Document doc in acDocs)
  11.             {
  12.                 doc.CommandEnded += doc_CommandEnded;
  13.             }
  14.  
  15.             // register acDocs_DocumentCreated for all futurely created documents
  16.             acDocs.DocumentCreated += acDocs_DocumentCreated;
  17.         }
  18.  
  19.         public void Terminate()
  20.         {
  21.         }
  22.  
  23.         void acDocs_DocumentCreated(object sender, DocumentCollectionEventArgs e)
  24.         {
  25.             // register doc_CommandEnded for a newly created document
  26.             e.Document.CommandEnded += doc_CommandEnded;
  27.         }
  28.  
  29.         void doc_CommandEnded(object sender, CommandEventArgs e)
  30.         {
  31.             if (Utils.WcMatch(e.GlobalCommandName.ToUpper(),
  32.                "*CHSPACE,*MSPACE,*PSPACE,*LAYOUT_CONTROL,U,*UNDO,*VPMAX,*VPMIN"))
  33.             {
  34.                 Reconcile();
  35.             }
  36.         }
  37.  
  38.         private void Reconcile()
  39.         {
  40.             Document doc = acDocs.MdiActiveDocument;
  41.             Editor ed = doc.Editor;
  42.  
  43.             short tilemode = (short)acApp.GetSystemVariable("TILEMODE");
  44.  
  45.             // Model space
  46.             if (tilemode == 1)
  47.             {
  48.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  49.             }
  50.  
  51.             else
  52.             {
  53.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  54.                 // Pviewport
  55.                 if (cvport > 1)
  56.                 {
  57.                     ed.WriteMessage("\n** PViewport active ** \n");
  58.                 }
  59.  
  60.                 // Paper space
  61.                 else
  62.                 {
  63.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  64.                 }
  65.             }
  66.         }
  67.     }
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #17 on: October 07, 2012, 06:26:29 AM »
Here's, IMO, a simpler way:
Code - C#: [Select]
  1.         public void Initialize()
  2.         {
  3.             LayoutManager.Current.LayoutSwitched += OnLayoutSwitched;
  4.         }
  5.  
  6.         public void Terminate()
  7.         {
  8.         }
  9.  
  10.         void OnLayoutSwitched(object sender, LayoutEventArgs e)
  11.         {
  12.             Editor ed = acApp.DocumentManager.MdiActiveDocument.Editor;
  13.             short tilemode = (short)acApp.GetSystemVariable("TILEMODE");
  14.             if (tilemode == 1)
  15.             {
  16.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  17.             }
  18.             else
  19.             {
  20.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  21.                 if (cvport > 1)
  22.                 {
  23.                     ed.WriteMessage("\n** PViewport active ** \n");
  24.                 }
  25.                 else
  26.                 {
  27.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  28.                 }
  29.             }
  30.         }
  31.     }
« Last Edit: October 07, 2012, 10:15:45 AM by gile »
Speaking English as a French Frog

TheMaster

  • Guest
Re: Object Events
« Reply #18 on: October 07, 2012, 08:42:45 AM »
Tony,
Quote
the removing and then immediately adding back event handlers
This is a practice I use to insure an event handler is not registered more than once for example within a Documentactivated event handler :
Code - C#: [Select]
  1.         void docMan_DocumentActivated(object sender, DocumentCollectionEventArgs e)
  2.         {
  3.             e.Document.CommandEnded -= doc_CommandEnded;
  4.             e.Document.CommandEnded += doc_CommandEnded;
  5.         }
Let me know if you think it is not a good practice and which one one you think is a better one.

RenderMan,
IMO, using both Documentactivated and DocumentCreated handlers is redundant.
If you used DocumentCreated, you do not need to remove the handler before adding it as the handler sould be add only once for a newly opened or created document.
If i do not misunderstand what you are trying to do, you want to register a CommandEnded event handler to all already opened documents when your application is loaded and to all those which may be created after.
Here's a way:
Code - C#: [Select]
  1.     public class Events : IExtensionApplication
  2.     {
  3.         private DocumentCollection acDocs;
  4.  
  5.         public void Initialize()
  6.         {
  7.             acDocs = acApp.DocumentManager;
  8.  
  9.             // register doc_CommandEnded for all already opened documents
  10.             foreach (Document doc in acDocs)
  11.             {
  12.                 doc.CommandEnded += doc_CommandEnded;
  13.             }
  14.  
  15.             // register acDocs_DocumentCreated for all futurely created documents
  16.             acDocs.DocumentCreated += acDocs_DocumentCreated;
  17.         }
  18.  
  19.         public void Terminate()
  20.         {
  21.         }
  22.  
  23.         void acDocs_DocumentCreated(object sender, DocumentCollectionEventArgs e)
  24.         {
  25.             // register doc_CommandEnded for a newly created document
  26.             e.Document.CommandEnded += doc_CommandEnded;
  27.         }
  28.  
  29.         void doc_CommandEnded(object sender, CommandEventArgs e)
  30.         {
  31.             if (Utils.WcMatch(e.GlobalCommandName.ToUpper(),
  32.                "*CHSPACE,*MSPACE,*PSPACE,*LAYOUT_CONTROL,U,*UNDO,*VPMAX,*VPMIN"))
  33.             {
  34.                 Reconcile();
  35.             }
  36.         }
  37.  
  38.         private void Reconcile()
  39.         {
  40.             Document doc = acDocs.MdiActiveDocument;
  41.             Editor ed = doc.Editor;
  42.  
  43.             short tilemode = (short)acApp.GetSystemVariable("TILEMODE");
  44.  
  45.             // Model space
  46.             if (tilemode == 1)
  47.             {
  48.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  49.             }
  50.  
  51.             else
  52.             {
  53.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  54.                 // Pviewport
  55.                 if (cvport > 1)
  56.                 {
  57.                     ed.WriteMessage("\n** PViewport active ** \n");
  58.                 }
  59.  
  60.                 // Paper space
  61.                 else
  62.                 {
  63.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  64.                 }
  65.             }
  66.         }
  67.     }

Gile, as I mentioned, I've seen managed objects throw exceptions when trying to remove an event handler that was never added, and I don't recall which events they were, but that's good enough reason for me to avoid removing handlers that weren't added.

I think the best way to deal with that problem is to structure the code so that what must be done once and un-done once is isolated in separate methods, and can be controlled using a boolean field.


gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #19 on: October 07, 2012, 10:26:06 AM »
Thank you for your reply Tony.
May be a  try/finally block would be easier than a boolen field in some cases.
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #20 on: October 07, 2012, 01:20:08 PM »
RenderMan,

The last code snippet I posted do not catch switches between PViewport and PaperSpace by double clicking.
If you need to catch this too, you can play with both LayoutManager.LayoutSwitched and Application.SystemVariableChanged events:

Code - C#: [Select]
  1. public class Events : IExtensionApplication
  2.     {
  3.         private DocumentCollection acDocs;
  4.  
  5.         public void Initialize()
  6.         {
  7.             acDocs = acApp.DocumentManager;
  8.             LayoutManager.Current.LayoutSwitched += OnLayoutSwitched;
  9.             acApp.SystemVariableChanged += acApp_SystemVariableChanged;
  10.         }
  11.  
  12.         public void Terminate()
  13.         {
  14.         }
  15.  
  16.         void acApp_SystemVariableChanged(
  17.             object sender,
  18.             Autodesk.AutoCAD.ApplicationServices.SystemVariableChangedEventArgs e)
  19.         {
  20.             if (e.Name == "CVPORT")
  21.             {
  22.                 Editor ed = acDocs.MdiActiveDocument.Editor;
  23.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  24.                 if (cvport > 1)
  25.                 {
  26.                     ed.WriteMessage("\n** PViewport active ** \n");
  27.                 }
  28.                 else
  29.                 {
  30.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  31.                 }
  32.             }
  33.         }
  34.  
  35.         void OnLayoutSwitched(object sender, LayoutEventArgs e)
  36.         {
  37.             Editor ed = acApp.DocumentManager.MdiActiveDocument.Editor;
  38.             short tilemode = (short)acApp.GetSystemVariable("TILEMODE");
  39.             if (tilemode == 1)
  40.             {
  41.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  42.             }
  43.             else
  44.             {
  45.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  46.                 if (cvport == 1)
  47.                 {
  48.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  49.                 }
  50.             }
  51.         }
  52.     }
Speaking English as a French Frog

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #21 on: October 07, 2012, 03:16:09 PM »
First - I just wanted to say thanks, Tony, and Gile.

As for identifying which Document has been registered using a bool Property is one of the samples I first found when lookin into all of this. I'll revisit that.

As for using LayoutSwitched && SystemVariableChanged; this will not function properly in MDI, as when one Document is in ModelSpace, and a second is in Layout with PViewport Active, switching between them does not change a Document level System Variable. Not sure if LayoutSwitched will fire, as I have not tested.

Hence my original need for both the DocumentCreated event (to register), and the DocumentActivated event (to evaluate the current space, or reconcile)... Each performed a different function.

Further adding to the mix, as I am attempting to enhance my VL routine, is that I have just learned that not even .NET API has direct exposure to the Preferences Objects, and must use COM first before accessing PreferencesDisplay, etc.

Not a big deal on the surface, but I am again disappointed to learn that I cannot simply monitor a Preferences*Modified event. If I am correct in my understanding, then even my .NET adaptation suffers similar limitations for accounting for user changes via Options dialog or VL functions which change Preferences* Objects outside of this plug-in.

** Edit to add - I know that I could simply monitor the Options CommandEnded && CommandWillStart, and all Lisp* event(s), but I guess I feel that I shouldn't have to. Such a critical object (such as preferences) should have a means by which to monitor changes. Am I just oblivious to a known method for doing this?

I'm going to have to give this more thought moving forward. Thank you again, for all of your assistance and feedback.
« Last Edit: October 07, 2012, 03:25:08 PM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #22 on: October 07, 2012, 03:34:31 PM »
I've only, what I consider to be, recently started learning the .NET API... Given that so much of this is new to me anyway, I wonder if I should just start learning C++/ObjectARX. I know that there's development concepts and some syntax that overlap or are similar respectively; perhaps if I'm going to start the long, long process of learning a higher level language, I should just go for the biggest, baddest one.

I know that Tony has ARX experience (not sure about Jeff & Gile?)... Perhaps you could offer some input? I'm seeking to transition from CAD Production into more development. I'm sure I must be over simplifying the tasks for the purposes of my questions here, but I could use some guidance, as I simply do not know enough for myself (yet).

Cheers! :beer:
"How we think determines what we do, and what we do determines what we get."

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #23 on: October 07, 2012, 04:40:51 PM »
Quote
As for using LayoutSwitched && SystemVariableChanged; this will not function properly in MDI, as when one Document is in ModelSpace, and a second is in Layout with PViewport Active, switching between them does not change a Document level System Variable.

If needed, you can handle the DocumentManager.DocumentActivated too:

Code - C#: [Select]
  1. public class Events : IExtensionApplication
  2.     {
  3.         private DocumentCollection acDocs;
  4.  
  5.         public void Initialize()
  6.         {
  7.             acDocs = acApp.DocumentManager;
  8.             LayoutManager.Current.LayoutSwitched += OnLayoutSwitched;
  9.             acApp.SystemVariableChanged += acApp_SystemVariableChanged;
  10.             acDocs.DocumentActivated += acDocs_DocumentActivated;
  11.         }
  12.  
  13.         public void Terminate()
  14.         {
  15.         }
  16.  
  17.         void acDocs_DocumentActivated(object sender, DocumentCollectionEventArgs e)
  18.         {
  19.             Document doc = e.Document;
  20.             Editor ed = doc.Editor;
  21.             short tilemode = (short)acApp.GetSystemVariable("TILEMODE");
  22.             if (tilemode == 1)
  23.             {
  24.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  25.             }
  26.             else
  27.             {
  28.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  29.                 if (cvport == 1)
  30.                 {
  31.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  32.                 }
  33.                 else
  34.                 {
  35.                     ed.WriteMessage("\n** PViewport active ** \n");
  36.                 }
  37.             }
  38.  
  39.         }
  40.  
  41.         void acApp_SystemVariableChanged(
  42.             object sender,
  43.             Autodesk.AutoCAD.ApplicationServices.SystemVariableChangedEventArgs e)
  44.         {
  45.             if (e.Name == "CVPORT")
  46.             {
  47.                 Editor ed = acDocs.MdiActiveDocument.Editor;
  48.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  49.                 if (cvport == 1)
  50.                 {
  51.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  52.                 }
  53.                 else
  54.                 {
  55.                     ed.WriteMessage("\n** PViewport active ** \n");
  56.                 }
  57.             }
  58.         }
  59.  
  60.         void OnLayoutSwitched(object sender, LayoutEventArgs e)
  61.         {
  62.             Editor ed = acApp.DocumentManager.MdiActiveDocument.Editor;
  63.             short tilemode = (short)acApp.GetSystemVariable("TILEMODE");
  64.             if (tilemode == 1)
  65.             {
  66.                 ed.WriteMessage("\n** ModelSpace active ** \n");
  67.             }
  68.             else
  69.             {
  70.                 short cvport = (short)acApp.GetSystemVariable("CVPORT");
  71.                 if (cvport == 1)
  72.                 {
  73.                     ed.WriteMessage("\n** PaperSpace active ** \n");
  74.                 }
  75.             }
  76.         }
  77.     }
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #24 on: October 07, 2012, 05:26:08 PM »
The same as upper with some refactorisation:

Code - C#: [Select]
  1.     public class Events : IExtensionApplication
  2.     {
  3.         private DocumentCollection acDocs;
  4.  
  5.         public void Initialize()
  6.         {
  7.             acDocs = acApp.DocumentManager;
  8.             LayoutManager.Current.LayoutSwitched += OnLayoutSwitched;
  9.             acApp.SystemVariableChanged += OnSystemVariableChanged;
  10.             acDocs.DocumentActivated += OnDocumentActivated;
  11.         }
  12.  
  13.         public void Terminate()
  14.         {
  15.         }
  16.  
  17.         private void OnDocumentActivated(object sender, DocumentCollectionEventArgs e)
  18.         {
  19.             if (!(IsModelSpace() || IsPaperSpace()))
  20.             {
  21.                 SendMessage("PViewport");
  22.             }
  23.         }
  24.  
  25.         private void OnSystemVariableChanged(
  26.             object sender,
  27.             Autodesk.AutoCAD.ApplicationServices.SystemVariableChangedEventArgs e)
  28.         {
  29.             if (e.Name == "CVPORT" && !IsPaperSpace())
  30.             {
  31.                 SendMessage("PViewport");
  32.             }
  33.         }
  34.  
  35.         private void OnLayoutSwitched(object sender, LayoutEventArgs e)
  36.         {
  37.             if (!IsModelSpace())
  38.             {
  39.                 IsPaperSpace();
  40.             }
  41.         }
  42.  
  43.         private bool IsModelSpace()
  44.         {
  45.             short tilemode = (short)acApp.GetSystemVariable("TILEMODE");
  46.             if (tilemode == 1)
  47.             {
  48.                 SendMessage("ModelSpace");
  49.                 return true;
  50.             }
  51.             return false;
  52.         }
  53.  
  54.         private bool IsPaperSpace()
  55.         {
  56.             short cvport = (short)acApp.GetSystemVariable("CVPORT");
  57.             if (cvport == 1)
  58.             {
  59.                 SendMessage("PaperSpace");
  60.                 return true;
  61.             }
  62.             return false;
  63.         }
  64.  
  65.         private void SendMessage(string msg)
  66.         {
  67.             acDocs.MdiActiveDocument.Editor.WriteMessage("\n** {0} active ** \n", msg);
  68.         }
  69.     }
« Last Edit: October 07, 2012, 05:36:00 PM by gile »
Speaking English as a French Frog

TheMaster

  • Guest
Re: Object Events
« Reply #25 on: October 07, 2012, 07:40:16 PM »

Further adding to the mix, as I am attempting to enhance my VL routine, is that I have just learned that not even .NET API has direct exposure to the Preferences Objects, and must use COM first before accessing PreferencesDisplay, etc.

Not a big deal on the surface, but I am again disappointed to learn that I cannot simply monitor a Preferences*Modified event. If I am correct in my understanding, then even my .NET adaptation suffers similar limitations for accounting for user changes via Options dialog or VL functions which change Preferences* Objects outside of this plug-in.


Application.UserConfigurationManager.CurrentProfileChanged  perhaps?

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #26 on: October 07, 2012, 08:14:18 PM »
I'll have to look into Application.UserConfigurationManager.CurrentProfileChanged; as always, thank you kindly for the suggestion, Tony.
"How we think determines what we do, and what we do determines what we get."

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Object Events
« Reply #27 on: October 13, 2012, 07:35:54 PM »
Further adding to the mix, as I am attempting to enhance my VL routine, is that I have just learned that not even .NET API has direct exposure to the Preferences Objects, and must use COM first before accessing PreferencesDisplay, etc.

I just ran across this which I guess I started and forgot about. Might be helpful and could put in whatever settings you need so not to have to reference the Interop assemblies.
 
I think the DLR will check if it has created the object previously to help performance, and I do not think the way I did it helps anything, but it makes me feel better by believing if I put each of the main objects in a property that once jitted it will help it remember it and run quicker(Just superstition).
 
Need 4.0
Code - C#: [Select]
  1. using System;
  2. using System.IO;
  3. namespace Autodesk.AutoCAD.ApplicationServices
  4. {
  5.     public class AcadApplication
  6.     {
  7.         private static dynamic _acadApplication { get { return Application.AcadApplication; } }
  8.         public class Preferences
  9.         {
  10.             private static dynamic _preferences { get { return _acadApplication.Preferences; } }
  11.            
  12.  
  13.             public class Files
  14.             {
  15.                 private static dynamic _files { get { return _preferences.Files; } }
  16.                 public static string QNewTemplateFile
  17.                 {
  18.                     get
  19.                     {
  20.                         return _files.QNewTemplateFile;
  21.                     }
  22.                     set
  23.                     {
  24.                         string filePath = string.Empty;
  25.                         if (!value.IsNullOrWhiteSpace())
  26.                         {
  27.                             if (Path.GetExtension(value).ToLower() != ".dwt")
  28.                             {
  29.                                 throw new ArgumentException("Must be a .dwt file", value);
  30.                             }
  31.                             if (!File.Exists(value))
  32.                             {
  33.                                 throw new FileNotFoundException(value);
  34.                             }
  35.                             filePath = value;
  36.                         }
  37.  
  38.                         _files.QNewTemplateFile = filePath;
  39.                     }
  40.                 }
  41.                 public static string TemplateDwgPath
  42.                 {
  43.                     get
  44.                     {
  45.                         return _files.TemplateDwgPath;
  46.                     }
  47.                     set
  48.                     {
  49.                         if (!Directory.Exists(value))
  50.                         {
  51.                             throw new FileNotFoundException(value);
  52.                         }
  53.  
  54.                         _files.TemplateDwgPath = value;
  55.                     }
  56.                 }
  57.                 public static string TempFilePath
  58.                 {
  59.                     get
  60.                     {
  61.                         return _files.TempFilePath;
  62.                     }
  63.                     set
  64.                     {
  65.                         if (!Directory.Exists(value))
  66.                         {
  67.                             throw new FileNotFoundException(value);
  68.                         }
  69.  
  70.                         _files.TempFilePath = value;
  71.                     }
  72.                 }
  73.  
  74.             }
  75.             public class Profiles
  76.             {
  77.                 private static dynamic _profiles { get { return _preferences.Profiles; } }
  78.                 public static string ActiveProfile
  79.                 {
  80.                     get
  81.                     {
  82.                         return _profiles.ActiveProfile;
  83.                     }
  84.                     set
  85.                     {
  86.                         _profiles.ActiveProfile = value;
  87.                     }
  88.                 }
  89.                 public static string[] GetAllProfileNames()
  90.                 {
  91.                     string[] names;
  92.                     _profiles.GetAllProfileNames(out names);
  93.                     return names;
  94.                 }
  95.             }
  96.         }
  97.     }
  98. }
  99.  

So it can be used as
Code - C#: [Select]
  1.  
  2.         //Set path no nothing
  3.         [CommandMethod("Testes")]
  4.         public void Testes()
  5.         {
  6.             AcadApplication.Preferences.Files.QNewTemplateFile = string.Empty;
  7.         }
  8.         //Not a real path
  9.         [CommandMethod("Testes1")]
  10.         public void Testes1()
  11.         {
  12.             AcadApplication.Preferences.Files.QNewTemplateFile = @"J:\Not\A\Real\Path\Base.dwt"; //Throw error
  13.         }
  14.         //Not a .dwt
  15.         [CommandMethod("Testes2")]
  16.         public void Testes2()
  17.         {
  18.             AcadApplication.Preferences.Files.QNewTemplateFile = @"J:\CadMgd\HGCE\Imperial\Templates\Base.dwg"; //Throw error
  19.  
  20.         }
  21.         //Set the path
  22.         [CommandMethod("Testes3")]
  23.         public void Testes3()
  24.         {
  25.             AcadApplication.Preferences.Files.QNewTemplateFile = @"J:\CadMgd\HGCE\Imperial\Templates\Base.dwt";
  26.         }
  27.         //Write Active Profile to commandLine
  28.         [CommandMethod("Testes4")]
  29.         public void Testes4()
  30.         {
  31.             Ed.WriteLine(AcadApplication.Preferences.Profiles.ActiveProfile);
  32.         }
  33.  

TheMaster

  • Guest
Re: Object Events
« Reply #28 on: October 13, 2012, 08:43:43 PM »
Further adding to the mix, as I am attempting to enhance my VL routine, is that I have just learned that not even .NET API has direct exposure to the Preferences Objects, and must use COM first before accessing PreferencesDisplay, etc.

I just ran across this which I guess I started and forgot about. Might be helpful and could put in whatever settings you need so not to have to reference the Interop assemblies.
 

Other than for the sake of limited Intellisense support, I'm not sure all of that is necessary

Code - C#: [Select]
  1.  
  2. public static void Example()
  3. {
  4.     string path = ( (dynamic) Application.Preferences ).Files.AutoSavePath;    
  5. }
  6.  
  7.  

All you have to do is cast the AcadApplication object to dynamic.

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #29 on: October 13, 2012, 09:14:30 PM »
Jeff / Tony -

Forgive my elementary question here; just trying to better understand....

I've read that when using the dynamic type, "you do not have to be concerned about whether the object gets its value from a COM API, from a dynamic language such as IronPython, from the HTML Document Object Model (DOM), from reflection, or from somewhere else," but I do not understand how or why one would not need to reference the Interop assembly.

How does one access the Application.AcadApplication Object without the appropriate reference, and only System[.*] using statements?
"How we think determines what we do, and what we do determines what we get."

TheMaster

  • Guest
Re: Object Events
« Reply #30 on: October 14, 2012, 01:50:23 AM »
Jeff / Tony -

Forgive my elementary question here; just trying to better understand....

I've read that when using the dynamic type, "you do not have to be concerned about whether the object gets its value from a COM API, from a dynamic language such as IronPython, from the HTML Document Object Model (DOM), from reflection, or from somewhere else," but I do not understand how or why one would not need to reference the Interop assembly.

How does one access the Application.AcadApplication Object without the appropriate reference, and only System[.*] using statements?

The interop assembly defines a statically-typed wrapper for COM types, which allows them to be treated like they were managed types (e.g., you get intellisense and static type-checking).

If you don't reference the interop assemblies, then you use the AcadApplication property of the Application object directly, which is typed as System.Object, and requires that you use either reflection or dynamic to access it.

Using reflection involves calling GetType().InvokeMember("membername", blah blah blah ) (you can find plenty of examples of late-bound use of AutoCAD COM objects via reflection here, by searching for 'GetType().InvokeMember'). 

With dynamic, the runtime basically does all of that same work that you must do manually when using reflection.

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #31 on: October 14, 2012, 12:29:21 PM »
That's bloody brilliant (the way dynamic works), and yet another documentation issue.

Thank you very much for the clarification, Tony.
"How we think determines what we do, and what we do determines what we get."

Jeff_M

  • King Gator
  • Posts: 4096
  • C3D user & customizer
Re: Object Events
« Reply #32 on: October 14, 2012, 12:57:17 PM »
... and yet another documentation issue.
?? This is a 'new to .NET 4' type. It's documented fairly well, imho, in the .NET docs. I first learned of it on Kean's blog when he was first discussing 2013's new API features.

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #33 on: October 14, 2012, 01:45:35 PM »
My mistake; the link to the Preferences* COM Object reference in the documentation I posted previously was for earlier than 2012, specifically coding for 2011 (.NET 3.5).
"How we think determines what we do, and what we do determines what we get."

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Object Events
« Reply #34 on: October 14, 2012, 02:56:44 PM »
Further adding to the mix, as I am attempting to enhance my VL routine, is that I have just learned that not even .NET API has direct exposure to the Preferences Objects, and must use COM first before accessing PreferencesDisplay, etc.

I just ran across this which I guess I started and forgot about. Might be helpful and could put in whatever settings you need so not to have to reference the Interop assemblies.
 

Other than for the sake of limited Intellisense support, I'm not sure all of that is necessary

Code - C#: [Select]
  1.  
  2. public static void Example()
  3. {
  4.     string path = ( (dynamic) Application.Preferences ).Files.AutoSavePath;  
  5. }
  6.  
  7.  

All you have to do is cast the AcadApplication object to dynamic.
Intellisense was probably way more motivation then should have been(if any is good),
but would
"I was thinking it would only take a little more code and just add properties as I need them, and doing so keeps from future spelling errors, etc..
also gives ability to validate in set method, and to return a different Type if easier to work with for example - properties that might return a string of path names delimited by a semi-colon gives the ability to return a collection and in 'set' concate back."
 
argument hold up in court?
 
Or as someone who has plenty of efficent, robust code running on many machines, is that a little too much?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #35 on: October 14, 2012, 03:23:54 PM »
My mistake; the link to the Preferences* COM Object reference in the documentation I posted previously was for earlier than 2012, specifically coding for 2011 (.NET 3.5).

If you can't use the dynamic type, to avoid writing every time: "GetType().InvokeMember("membername", blah blah blah )" with late binding  and refelection, you can use some extension methods which make the code more legible, there're many examples other there, here's one (late binding helper).
Using these methods allows to write code in a way similar to the vlisp functions: vlax-get, vlax-put and vlax-invoke.
Code - C#: [Select]
  1. string path = (string)Application.AcadApplication
  2.     .Get("Preferences")
  3.     .Get("Files")
  4.     .Get("AutoSavePath");
Speaking English as a French Frog

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #36 on: October 14, 2012, 04:45:25 PM »
You make a great point, in that I should start building a good code library; an extension method might be just what I am after in being able to access these COM dependent objects without hard-coding any dependencies.

Just to confirm then... .NET 3.5 (2011), I will still require the Interop reference and methodology described in my earlier post (as specified in the documentation linked)?
"How we think determines what we do, and what we do determines what we get."

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Object Events
« Reply #37 on: October 14, 2012, 04:52:29 PM »
Look at Tony's Save extension methods here
It shows using a dynamic object ---- requires 4.0
and shows using reflection ----- pre 4.0

BlackBox

  • King Gator
  • Posts: 3770
Re: Object Events
« Reply #38 on: October 14, 2012, 04:53:55 PM »
Look at Tony's Save extension methods here
It shows using a dynamic object ---- requires 4.0
and shows using reflection ----- pre 4.0

Thanks for the direction, Jeff... And thanks to Tony for writing the extension methods.

"How we think determines what we do, and what we do determines what we get."

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Object Events
« Reply #39 on: October 14, 2012, 05:20:17 PM »
Just to confirm then... .NET 3.5 (2011), I will still require the Interop reference and methodology described in my earlier post (as specified in the documentation linked)?

No the interest with late binding (or dynamic) is that you do not need to reference the Interop libraries (which are version and platform dependant).
The main inconvenient, as said before, is that you won't have help from Visual Studio at editing and compiling time (intellisense). If there's an error in the code (i.e. a typo in a method or property or a bad argument type) the exception will raise at runnig time.
Speaking English as a French Frog

lCine7ic

  • Guest
Re: Object Events
« Reply #40 on: October 23, 2012, 04:48:50 PM »
Tony,
Quote
the removing and then immediately adding back event handlers
This is a practice I use to insure an event handler is not registered more than once for example within a Documentactivated event handler :
Code - C#: [Select]
  1.         void docMan_DocumentActivated(object sender, DocumentCollectionEventArgs e)
  2.         {
  3.             e.Document.CommandEnded -= doc_CommandEnded;
  4.             e.Document.CommandEnded += doc_CommandEnded;
  5.         }
Let me know if you think it is not a good practice and which one one you think is a better one.

Just an FYI,

I made a custom class containing each Document object. 
This custom class has private variables of [EventStatus] type (see below):
eCurrentEventStatus
eLastEventStatus
Code - Visual Basic: [Select]
  1.     <Flags()> _
  2.     Friend Enum EventStatus As Integer
  3.         CommandWillStart = 1
  4.         CommandCancelled = 2
  5.         CommandFailed = 4
  6.         CommandEnded = 8
  7.         CommandFinish = CommandCancelled + CommandFailed + CommandEnded
  8.         SelectionAdded = 16
  9.         SelectionRemoved = 32
  10.         SelectionEvents = SelectionAdded + SelectionRemoved
  11.         ObjectErased = 64
  12.         ObjectOpenedForModify = 128
  13.         ObjectModified = 256
  14.         ObjectChangedEvents = ObjectErased + ObjectOpenedForModify + ObjectModified
  15.         BeginDocumentClose = 512
  16.         SaveBegin = 1024
  17.         SaveComplete = 2048
  18.         SaveAborted = 4096
  19.         SaveEndEvents = SaveComplete + SaveAborted
  20.         SaveEvents = SaveBegin + SaveComplete + SaveAborted
  21.         ImpliedSelectionChanged = 8192
  22.     End Enum
  23.  
  24.     Friend Enum ForceEvent
  25.         ActivateOnly = 1
  26.         DeactivateOnly = 2
  27.         SetTo = 3
  28.     End Enum
The class also has utility subroutines to add/remove event handlers -- The ImpliedSelectionChanged Events are shown below:
Code - Visual Basic: [Select]
  1.     Friend Overloads Sub ActivateImpliedSelectionChangedEvent(Optional ByVal ShowStatus As Boolean = False)
  2.         If CBool(eCurrentEventStatus And EventStatus.ImpliedSelectionChanged) = False Then
  3.             AddHandler _Document.ImpliedSelectionChanged, AddressOf H_ImpliedSelectionChanged
  4.             eCurrentEventStatus += EventStatus.ImpliedSelectionChanged
  5.             If ShowStatus Then Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbLf + "Activated Object ImpliedSelectionChanged")
  6.         Else
  7.             If ShowStatus Then Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbLf + "ImpliedSelectionChange Was Already Active")
  8.         End If
  9.     End Sub
  10.     Friend Overloads Sub DeactivateImpliedSelectionChangedEvent(Optional ByVal ShowStatus As Boolean = False)
  11.         If CBool(eCurrentEventStatus And EventStatus.ImpliedSelectionChanged) Then
  12.             Try : RemoveHandler _Document.ImpliedSelectionChanged, AddressOf H_ImpliedSelectionChanged : Catch ex As System.Exception : End Try
  13.             eCurrentEventStatus -= EventStatus.ImpliedSelectionChanged
  14.             If ShowStatus Then Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbLf + "Deactivated Object ImpliedSelectionChanged")
  15.         Else
  16.             If ShowStatus Then Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbLf + "ImpliedSelectionChange Was Not Active")
  17.         End If
  18.     End Sub

I use these Activate/Deactivate Subroutines exclusively to add/remove event handlers from documents.  One way I do this is using the SetEvents Function below
Code - Visual Basic: [Select]
  1. Friend Function SetEvents(Optional ByVal ForceOption As ForceEvent = ForceEvent.ActivateOnly, Optional ByVal ForceStatus As EventStatus = 0) As EventStatus
  2.     Dim eStatusToSet As EventStatus = 0
  3.     Dim eWas As EventStatus = eCurrentEventStatus
  4.     If CInt(ForceStatus) > -1 Then eStatusToSet = ForceStatus Else eStatusToSet = eLastEventStatus
  5.     Select Case ForceOption
  6.         Case ForceEvent.ActivateOnly
  7.             If (eStatusToSet And EventStatus.BeginDocumentClose) Then ActivateDocBeginCloseEvent()
  8.             If (eStatusToSet And EventStatus.ImpliedSelectionChanged) Then ActivateImpliedSelectionChangedEvent()
  9.             If (eStatusToSet And EventStatus.ObjectErased) Then ActivateObjectErasedEvent()
  10.             If (eStatusToSet And EventStatus.ObjectModified) Then ActivateObjectModified()
  11.             If (eStatusToSet And EventStatus.ObjectOpenedForModify) Then ActivateObjectOpenedForModify()
  12.             If (eStatusToSet And EventStatus.CommandWillStart) Then ActivateCmdWillStartEvent()
  13.             If (eStatusToSet And EventStatus.CommandFinish) Then ActivateCmdEvents()
  14.         Case ForceEvent.DeactivateOnly
  15.             If (eStatusToSet And EventStatus.BeginDocumentClose) = False Then DeactivateDocBeginCloseEvent()
  16.             If (eStatusToSet And EventStatus.ImpliedSelectionChanged) = False Then DeactivateImpliedSelectionChangedEvent()
  17.             If (eStatusToSet And EventStatus.ObjectErased) = False Then DeactivateObjectErasedEvent()
  18.             If (eStatusToSet And EventStatus.ObjectModified) = False Then DeactivateObjectModified()
  19.             If (eStatusToSet And EventStatus.ObjectOpenedForModify) = False Then DeactivateObjectOpenedForModify()
  20.             If (eStatusToSet And EventStatus.CommandWillStart) = False Then DeactivateCmdWillStartEvent()
  21.             If (eStatusToSet And EventStatus.CommandFinish) = False Then DeactivateCmdEvents()
  22.         Case ForceEvent.SetTo
  23.             If (eStatusToSet And EventStatus.BeginDocumentClose) Then ActivateDocBeginCloseEvent() Else DeactivateDocBeginCloseEvent()
  24.             If (eStatusToSet And EventStatus.ImpliedSelectionChanged) Then ActivateImpliedSelectionChangedEvent() Else DeactivateImpliedSelectionChangedEvent()
  25.             If (eStatusToSet And EventStatus.ObjectErased) Then ActivateObjectErasedEvent() Else DeactivateObjectErasedEvent()
  26.             If (eStatusToSet And EventStatus.ObjectModified) Then ActivateObjectModified() Else DeactivateObjectModified()
  27.             If (eStatusToSet And EventStatus.ObjectOpenedForModify) Then ActivateObjectOpenedForModify() Else DeactivateObjectOpenedForModify()
  28.             If (eStatusToSet And EventStatus.CommandWillStart) Then ActivateCmdWillStartEvent() Else DeactivateCmdWillStartEvent()
  29.             If (eStatusToSet And EventStatus.CommandFinish) Then ActivateCmdEvents() Else DeactivateCmdEvents()
  30.     End Select
  31.     Return eWas
  32. End Function

So, using a framework like this allows me to do some relatively complex automation -- it's not perfect (nor optimized) yet but it currently meets my needs.