Author Topic: DocumentCollection Event Handler(s)  (Read 14267 times)

0 Members and 1 Guest are viewing this topic.

BlackBox

  • King Gator
  • Posts: 3770
DocumentCollection Event Handler(s)
« on: September 08, 2012, 12:49:24 PM »
I'm trying to develop an Event handler DocumentCollection Events, for several different purposes, not necessarily in the same solution.

I am adept at coding Visual LISP Reactors, and I know there's more to Event monitors than VL requires... Are there any good tutorials, or samples from which I can learn?

TIA

** Edit - Revised thread title, and OP for clarity.
« Last Edit: September 21, 2012, 08:46:02 AM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

Jeff_M

  • King Gator
  • Posts: 4094
  • C3D user & customizer
Re: DocumentMonitor
« Reply #1 on: September 08, 2012, 02:16:20 PM »
The ObjectARX kit has a good example of events in the EventsWatcher sample code. On my system it's here:
C:\ObjectARX 2012\samples\dotNet\EventsWatcher

TheMaster

  • Guest
Re: DocumentMonitor
« Reply #2 on: September 08, 2012, 06:59:02 PM »
I'm trying to develop a DocumentMonitor for handling DocumentManager Events, for several different purposes, not necessarily in the same solution.

I am adept at coding Visual LISP Reactors, and I know there's more to Event monitors than VL requires... Are there any good tutorials, or samples from which I can learn?

TIA

I use a class called DocumentManager that allows me to add/remove document-level events when documents are opened/closed. But there's two basic ways it's used. In some cases, the events for all documents are handled by a single object, and in other cases, there are multiple instances of an object, one for each open document, that are created when a document is added to the DocumentCollection, and discarded/disposed when the document is destroyed.

I think I may have posted some older iterations of that class here.

So, how to approach it depends on whether you want per-document instances of an object, where each instance is associated with a single document, and handles the events of the document, or if you want to have a single instance that handles events for all documents (or a static class that does the same).


BlackBox

  • King Gator
  • Posts: 3770
Re: DocumentMonitor
« Reply #3 on: September 08, 2012, 07:05:43 PM »
I should have checked the ObjectARX SDK before posting... the Civil 3D samples are pretty much the same (at least for the versions I have installed), so I sometimes discount them to search online.

For the task I am attempting to complete, I believe a single object would do, as I only need it to act once for each document when it is added/opened. Also thanks for reminding me that a single object is possible, as my initial thought was to create one for each document, which would not be necessary.

Not really sure of the difference or advantage between a single Object, and/or a Static Class in this context.

As always, thanks for the guidance, Jeff & Tony. :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: DocumentMonitor
« Reply #4 on: September 09, 2012, 02:46:31 PM »

BlackBox

  • King Gator
  • Posts: 3770
Re: DocumentMonitor
« Reply #5 on: September 10, 2012, 09:47:07 AM »
Thanks for the link, Jeff!
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: DocumentCollection Event Handler(s)
« Reply #6 on: September 19, 2012, 08:38:05 AM »
This is still a work in progress on my part. Currently, I am using a registry key to auto-[NET]load my assembly into each of my sessions, and 'register' my Event handler (+=) via IExtensionApplication.Initialize().

If all I am attempting to handle is the DocumentCreated Event, should I be using DocumentToBeDestroyed Event to clean up anything, or is relying on IExtensionApplication.Terminate() to remove (-=) the Event handler sufficient?

** Edit - For example, when saving & closing a document that has an Event handler.

I just want to make sure that I am not overlooking something important, or mishandling Events, etc. (no pun intended).

TIA
« Last Edit: September 21, 2012, 08:46:21 AM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

BillZndl

  • Guest
Re: DocumentCollection Events Handler(s)
« Reply #7 on: September 19, 2012, 09:57:17 AM »
This is still a work in progress on my part. Currently, I am using a registry key to auto-[NET]load my assembly into each of my sessions, and 'register' my Event handler (+=) via IExtensionApplication.Initialize().

If all I am attempting to handle is the DocumentCreated Event, should I be using DocumentToBeDestroyed Event to clean up anything, or is relying on IExtensionApplication.Terminate() to remove (-=) the Event handler sufficient?

** Edit - For example, when saving & closing a document that has an Event handler.

I just want to make sure that I am not overlooking something important, or mishandling Events, etc. (no pun intended).

TIA


If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.

Code: [Select]
void DocumentManager_DocumentCreated(object sender, DocumentCollectionEventArgs e)
        {           
            e.Document.CommandWillStart += new CommandEventHandler(Document_CommandWillStart);           
        }

        void DocumentManager_DocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
        {           
            if (e.Document != null)
                e.Document.CommandWillStart -= new CommandEventHandler(Document_CommandWillStart);               
        }


BlackBox

  • King Gator
  • Posts: 3770
Re: DocumentCollection Event Handler(s)
« Reply #8 on: September 19, 2012, 10:01:49 AM »
If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.

That's exactly what I needed to know... I had seen several examples (those posted above, and others), and only one or two included the DocumentToBeDestroyed Callback, let alone removed the Event handler. Not even SpiderInNet1 had it here (and they're usually on point, in my novice opinion)

Cheers! :beer:
« Last Edit: September 21, 2012, 08:46:31 AM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

BillZndl

  • Guest
Re: DocumentCollection Events Handler(s)
« Reply #9 on: September 19, 2012, 10:41:14 AM »
If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.

That's exactly what I needed to know... I had seen several examples (those posted above, and others), and only one or two included the DocumentToBeDestroyed Callback, let alone removed the Event handler. Not even SpiderInNet1 had it here (and they're usually on point, in my novice opinion)

Cheers! :beer:

Depending on what you are doing and when your app starts,
you may want to consider adding event handlers to any documents that may already be open (along with the rest of your event handlers).
Here's what I use, called from IExtensionApplication.Initialize().

Code: [Select]
public void AddEventHandlersToOpenDocs()
        {
            try
            {
                DocumentCollection documents = AcadApp.DocumentManager;

                foreach (Document Document in documents)
                {
                    Document.CommandWillStart += new CommandEventHandler(Document_CommandWillStart);                   
                }

                AcadApp.DocumentManager.DocumentActivated += new DocumentCollectionEventHandler(DocumentManager_DocumentActivated);               
                AcadApp.DocumentManager.DocumentCreated += new DocumentCollectionEventHandler(DocumentManager_DocumentCreated);
                AcadApp.DocumentManager.DocumentToBeDestroyed += new DocumentCollectionEventHandler(DocumentManager_DocumentToBeDestroyed);
            }
            catch (Exception exception)
            {
                MessageBox.Show(" EventHandlers kaputt! " + exception.Message);
            }
        }


BlackBox

  • King Gator
  • Posts: 3770
Re: DocumentCollection Event Handler(s)
« Reply #10 on: September 21, 2012, 08:28:19 AM »
My inexperience with .NET Events may have led to my not asking the appropriate question(s) earlier, so I will try to be more specific... I understand (now, thanks BillZndl) that Document Events must be unregistered (-=) for each Document prior to being Destroyed.

Quote from: Autodesk Exchange, AutoCAD, Help, AutoCAD .NET Developer's Guide, Use Events, Handle Document Events

Handle Document Events

Document object events are used to respond to the document window. When a document event is registered, it is only associated with the document object in which it is associated. So if an event needs to be registered with each document, you will want to use the DocumentCreated event of the DocumentCollection object to register events with each new or opened drawing.

<snip>

However, as I am only attempting to monitor DocumentCollection Events in order to invoke a simple Method (which uses FindFile() & System.IO), when the DocumentCreated Event and\or the DocumentActivated Event fires... If I am understanding this correctly:

Quote from: Autodesk Exchange, AutoCAD, Help, AutoCAD .NET Developer's Guide, Use Events, Handle DocumentCollection Events (.NET)

Handle DocumentCollection Events

DocumentCollection events, unlike Document object events, remain registered until AutoCAD is shutdown or until they are unregistered.

<snip>

... I need only to unregister (-=) the DocumentCollectionEventHandler(s) upon IExtensionApplication.Terminate(), since it is being registered (+=) upon IExtensionApplication.Initialize(), no?

Pseudo code:
Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.Runtime;
  4.  
  5. using acApp = Autodesk.AutoCAD.ApplicationServices.Application;
  6.  
  7. using System;
  8. using System.IO;
  9.  
  10. [assembly: ExtensionApplication(typeof(FOO.SampleEvents))]
  11.  
  12. namespace FOO
  13. {
  14.     public class SampleEvents : IExtensionApplication
  15.     {
  16.         //DocumentCollection acDocs;
  17.  
  18.         void IExtensionApplication.Initialize()
  19.         {
  20.             DocumentCollection acDocs = acApp.DocumentManager;
  21.  
  22.             acDocs.DocumentCreated +=
  23.                 new DocumentCollectionEventHandler(acDocs_DocumentCreated);
  24.  
  25.             acDocs.DocumentActivated +=
  26.                 new DocumentCollectionEventHandler(acDocs_DocumentActivated);
  27.  
  28.             acDocs.MdiActiveDocument.Editor.WriteMessage(
  29.                 "\n** DocumentCollectionEventHandler loaded ** \n"
  30.                 );
  31.         }
  32.  
  33.         void IExtensionApplication.Terminate()
  34.         {
  35.             DocumentCollection acDocs = acApp.DocumentManager;
  36.  
  37.             acDocs.DocumentCreated -=
  38.                 new DocumentCollectionEventHandler(acDocs_DocumentCreated);
  39.  
  40.             acDocs.DocumentActivated -=
  41.                 new DocumentCollectionEventHandler(acDocs_DocumentActivated);
  42.         }
  43.  
  44.         public void acDocs_DocumentCreated(Object sender, DocumentCollectionEventArgs e)
  45.         {
  46.             if (e.Document != null)
  47.             {
  48.                 e.Document.Editor.WriteMessage("\n** DocumentCreated: ({0}) ** \n",
  49.                     Path.GetFileName(e.Document.Name)
  50.                     );
  51.             }
  52.         }
  53.  
  54.         public void acDocs_DocumentActivated(Object sender, DocumentCollectionEventArgs e)
  55.         {
  56.             if (e.Document != null)
  57.             {
  58.                 e.Document.Editor.WriteMessage("\n** DocumentActivated: ({0}) ** \n",
  59.                     Path.GetFileName(e.Document.Name)
  60.                     );
  61.             }
  62.         }
  63.     }
  64. }
  65.  
« Last Edit: September 21, 2012, 08:47:03 AM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: DocumentCollection Event Handler(s)
« Reply #11 on: September 21, 2012, 01:52:38 PM »
... I need only to unregister (-=) the DocumentCollectionEventHandler(s) upon IExtensionApplication.Terminate(), since it is being registered (+=) upon IExtensionApplication.Initialize(), no?

Yes.

I use a shorthand version of event registering. Don't know what the difference is.
Code: [Select]
acDocs.DocumentCreated += acDocs_DocumentCreated;

and
Code: [Select]
acDocs.DocumentCreated -= acDocs_DocumentCreated;
« Last Edit: September 21, 2012, 01:57:50 PM by MexicanCustard »
Revit 2019, AMEP 2019 64bit Win 10

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: DocumentCollection Event Handler(s)
« Reply #12 on: September 21, 2012, 02:02:40 PM »
Not sure if it will cause a problem but without this class being static AutoCAD is going to want to create a new instance for every document you open. Thats going to register a stack of events which isn't what you want.  I'm not sure if this is what will happen with this code but that is what I think will happen.
Revit 2019, AMEP 2019 64bit Win 10

BlackBox

  • King Gator
  • Posts: 3770
Re: DocumentCollection Event Handler(s)
« Reply #13 on: September 21, 2012, 02:56:17 PM »
Not sure if it will cause a problem but without this class being static AutoCAD is going to want to create a new instance for every document you open. Thats going to register a stack of events which isn't what you want.  I'm not sure if this is what will happen with this code but that is what I think will happen.

I really don't know, but I would very much like to avoid that... Hence my seeking clarity.

The pseudo code I posted, was an adaptation of that which is provided in the online documentation (linked above):

Quote from: Autodesk Exchange, AutoCAD, Help, AutoCAD .NET Developer's Guide, Use Events, Handle DocumentCollection Events (.NET)
Code - C#: [Select]
  1. using Autodesk.AutoCAD.Runtime;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3.  
  4. [CommandMethod("AddDocColEvent")]
  5. public void AddDocColEvent()
  6. {
  7.   Application.DocumentManager.DocumentActivated +=
  8.       new DocumentCollectionEventHandler(docColDocAct);
  9. }
  10.  
  11. [CommandMethod("RemoveDocColEvent")]
  12. public void RemoveDocColEvent()
  13. {
  14.   Application.DocumentManager.DocumentActivated -=
  15.       new DocumentCollectionEventHandler(docColDocAct);
  16. }
  17.  
  18. public void docColDocAct(object senderObj,
  19.                          DocumentCollectionEventArgs docColDocActEvtArgs)
  20. {
  21.   Application.ShowAlertDialog(docColDocActEvtArgs.Document.Name +
  22.                               " was activated.");
  23. }
  24.  

Again, I'm just trying to make sure that I learn some best practices for Events; I know the documentation is notorious for being incomplete, etc. and wanted to see if I am headed in the right direction, and not making any blatant mistakes moving forward.
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: DocumentCollection Event Handler(s)
« Reply #14 on: September 22, 2012, 11:25:36 AM »
After some reading, I was able to find some clarification on this 'shorthand' syntax:

I use a shorthand version of event registering. Don't know what the difference is.
Code: [Select]
acDocs.DocumentCreated += acDocs_DocumentCreated;

and
Code: [Select]
acDocs.DocumentCreated -= acDocs_DocumentCreated;

... It's called  'method group conversion'; I've also included some additional excerpts from Andrew's book regarding listening to, and simplifying the registration of, Events:



Quote from: Andrew Troelsen - Pro C# 2010 and the .NET 4.0 Platform, Fifth Edition, Page 422

Listening to Incoming Events

C# events also simplify the act of registering the caller-side event handlers. Rather than having to specify
custom helper methods, the caller simply makes use of the += and -= operators directly (which triggers
the correct add_XXX() or remove_XXX() method in the background). When you wish to register with an
event, follow the pattern shown here:

Code - C#: [Select]
  1. // NameOfObject.NameOfEvent += new RelatedDelegate(functionToCall);
  2. //
  3. Car.EngineHandler d = new Car.CarEventHandler(CarExplodedEventHandler)
  4. myCar.Exploded += d;
  5. When you wish to detach from a source of events, use the -= operator, using the following pattern
  6. // NameOfObject.NameOfEvent -= new RelatedDelegate(functionToCall);
  7. //
  8. myCar.Exploded -= d;
  9.  

Given these very predictable patterns, here is the refactored Main() method, now using the C# event
registration syntax:

Code - C#: [Select]
  1.     class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             Console.WriteLine("***** Fun with Events *****\n");
  6.             Car c1 = new Car("SlugBug", 100, 10);
  7.  
  8.             // Register event handlers.
  9.             c1.AboutToBlow += new Car.CarEngineHandler(CarIsAlmostDoomed);
  10.             c1.AboutToBlow += new Car.CarEngineHandler(CarAboutToBlow);
  11.             Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded);
  12.  
  13.             c1.Exploded += d;
  14.  
  15.             Console.WriteLine("***** Speeding up *****");
  16.  
  17.             for (int i = 0; i < 6; i++)
  18.                 c1.Accelerate(20);
  19.  
  20.             // Remove CarExploded method
  21.             // from invocation list.
  22.             c1.Exploded -= d;
  23.  
  24.             Console.WriteLine("\n***** Speeding up *****");
  25.  
  26.             for (int i = 0; i < 6; i++)
  27.                 c1.Accelerate(20);
  28.  
  29.             Console.ReadLine();
  30.         }
  31.  
  32.         public static void CarAboutToBlow(string msg)
  33.         { Console.WriteLine(msg); }
  34.  
  35.         public static void CarIsAlmostDoomed(string msg)
  36.         { Console.WriteLine("=> Critical Message from Car: {0}", msg); }
  37.  
  38.         public static void CarExploded(string msg)
  39.         { Console.WriteLine(msg); }
  40.     }
  41.  

To even further simplify event registration, you can use method group conversion. Consider the
following iteration of Main():

Code - C#: [Select]
  1.         static void Main(string[] args)
  2.         {
  3.             Console.WriteLine("***** Fun with Events *****\n");
  4.             Car c1 = new Car("SlugBug", 100, 10);
  5.  
  6.             // Register event handlers.
  7.             c1.AboutToBlow += CarIsAlmostDoomed;
  8.             c1.AboutToBlow += CarAboutToBlow;
  9.             c1.Exploded += CarExploded;
  10.  
  11.             Console.WriteLine("***** Speeding up *****");
  12.  
  13.             for (int i = 0; i < 6; i++)
  14.                 c1.Accelerate(20);
  15.  
  16.             c1.Exploded -= CarExploded;
  17.  
  18.             Console.WriteLine("\n***** Speeding up *****");
  19.  
  20.             for (int i = 0; i < 6; i++)
  21.                 c1.Accelerate(20);
  22.  
  23.             Console.ReadLine();
  24.         }
  25.  



Simplifying Event Registration Using Visual Studio 2010

Visual Studio 2010 offers assistance with the process of registering event handlers. When you apply the
+= syntax during event registration, you will find an IntelliSense window displayed, inviting you to hit the
Tab key to autocomplete the associated delegate instance (see Figure 11-2)

(see 11-2_delegate_selection_intellisense.bmp)

After you hit the Tab key, you are invited to enter the name of the event handler to be generated (or
simply accept the default name) as shown in Figure 11-3.

(see 11-3_delegate_target_format_intellisense.bmp)

When you hit the Tab key again, you will be provided with stub code in the correct format of the
delegate target (note that this method has been declared static due to the fact that the event was
registered within the static Main() method):

Code - C#: [Select]
  1.         static void newCar_AboutToBlow(string msg)
  2.         {
  3.             // Add your code!
  4.         }
  5.  

IntelliSense is available to all .NET events in the base class libraries. This IDE feature is a massive
time-saver, given that it saves you from having to search the .NET help system to figure out both the
correct delegate to use with a particular event and the format of the delegate target method.
"How we think determines what we do, and what we do determines what we get."