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
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
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);
}
If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.
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 (http://spiderinnet1.typepad.com/blog/2011/12/autocad-net-documentcollection-events-document-creation-related.html) (and they're usually on point, in my novice opinion)
Cheers! :beer:
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);
}
}
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>
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?
acDocs.DocumentCreated += acDocs_DocumentCreated;
acDocs.DocumentCreated -= acDocs_DocumentCreated;
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.
Code - C#: [Select]
using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; [CommandMethod("AddDocColEvent")] public void AddDocColEvent() { Application.DocumentManager.DocumentActivated += } [CommandMethod("RemoveDocColEvent")] public void RemoveDocColEvent() { Application.DocumentManager.DocumentActivated -= } public void docColDocAct(object senderObj, DocumentCollectionEventArgs docColDocActEvtArgs) { Application.ShowAlertDialog(docColDocActEvtArgs.Document.Name + " was activated."); }
I use a shorthand version of event registering. Don't know what the difference is.Code: [Select]acDocs.DocumentCreated += acDocs_DocumentCreated;
andCode: [Select]acDocs.DocumentCreated -= acDocs_DocumentCreated;
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]
// NameOfObject.NameOfEvent += new RelatedDelegate(functionToCall); // myCar.Exploded += d; When you wish to detach from a source of events, use the -= operator, using the following pattern // NameOfObject.NameOfEvent -= new RelatedDelegate(functionToCall); // myCar.Exploded -= d;
Given these very predictable patterns, here is the refactored Main() method, now using the C# event
registration syntax:Code - C#: [Select]
class Program { static void Main(string[] args) { Console.WriteLine("***** Fun with Events *****\n"); // Register event handlers. c1.Exploded += d; Console.WriteLine("***** Speeding up *****"); for (int i = 0; i < 6; i++) c1.Accelerate(20); // Remove CarExploded method // from invocation list. c1.Exploded -= d; Console.WriteLine("\n***** Speeding up *****"); for (int i = 0; i < 6; i++) c1.Accelerate(20); Console.ReadLine(); } public static void CarAboutToBlow(string msg) { Console.WriteLine(msg); } public static void CarIsAlmostDoomed(string msg) { Console.WriteLine("=> Critical Message from Car: {0}", msg); } public static void CarExploded(string msg) { Console.WriteLine(msg); } }
To even further simplify event registration, you can use method group conversion. Consider the
following iteration of Main():Code - C#: [Select]
static void Main(string[] args) { Console.WriteLine("***** Fun with Events *****\n"); // Register event handlers. c1.AboutToBlow += CarIsAlmostDoomed; c1.AboutToBlow += CarAboutToBlow; c1.Exploded += CarExploded; Console.WriteLine("***** Speeding up *****"); for (int i = 0; i < 6; i++) c1.Accelerate(20); c1.Exploded -= CarExploded; Console.WriteLine("\n***** Speeding up *****"); for (int i = 0; i < 6; i++) c1.Accelerate(20); Console.ReadLine(); }
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]
static void newCar_AboutToBlow(string msg) { // Add your code! }
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.
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.
If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.
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.
Could you (or someone else) please elaborate on this, as I am not understanding how this could 'create a new instance for every document' opened? :?
You do not need to create multiple instances of a DocumentCollection event handler to run its event handler in each document.
Events that are members of the Document class are fired by the Document, only as long as it exists. Once a document is destroyed, all events and the handlers added to them are destroyed as well, and so after the document is destroyed, those events will never fire.
It's not necessary to remove handlers for document events if you want them to handle the events for the life of the document.
When a command method is not static/shared, AutoCAD creates multiple instances of the class that declares the method, one for each document the command is used in. In the case of that example, the code that adds the handlers to the events are in a command method, so they only way the events would be added multiple times is if the command was invoked multiple times.
I would be careful with those docs and the included sample code. Much of it is clearly the work of inexperienced writers.
If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.
Sorry, that's not true.
Events that are members of the Document class are fired by the Document, only as long as it exists. Once a document is destroyed, all events and the handlers added to them are destroyed as well, and so after the document is destroyed, those events will never fire.
It's not necessary to remove handlers for document events if you want them to handle the events for the life of the document.
Could you (or someone else) please elaborate on this, as I am not understanding how this could 'create a new instance for every document' opened? :?
If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.Sorry, that's not true.
Events that are members of the Document class are fired by the Document, only as long as it exists. Once a document is destroyed, all events and the handlers added to them are destroyed as well, and so after the document is destroyed, those events will never fire.
It's not necessary to remove handlers for document events if you want them to handle the events for the life of the document.
Examples I have seen by autodesk I can see how they might convey that.
The object that 'publishes' the events is the one that holds an reference to subscribers.
Subscribing to documents events will not cause the document object to live any longer or not get GC.
The document object will keep your class that hold the subscribed handlers from being collected until it is collected.
Would'nt Autocad have to be closing if your app is closing?Examples I have seen by autodesk I can see how they might convey that.
The object that 'publishes' the events is the one that holds an reference to subscribers.
Subscribing to documents events will not cause the document object to live any longer or not get GC.
The document object will keep your class that hold the subscribed handlers from being collected until it is collected.
And since the class that holds the subscribed handlers is static and lives until the App closes,
what happens if I close my app before autocad closes?
Will I get a Kaboom, because the documents are still alive?
If you add an event handler during the document created event, you should remove it when the Document to be destroyed event takes place.Sorry, that's not true.
Events that are members of the Document class are fired by the Document, only as long as it exists. Once a document is destroyed, all events and the handlers added to them are destroyed as well, and so after the document is destroyed, those events will never fire.
It's not necessary to remove handlers for document events if you want them to handle the events for the life of the document.
This begs a question:
If you can add an event handler to an open document and leave it there for the life of the document,
is there any benefit to removing event handlers from a document when the handler is not in use?
My Example: When my App initializes,
I add "Document.CommandWillStart += new CommandEventHandler(Document_CommandWillStart);" to all open documents in AutoCAD
and to any newly created documents during the session.
Then I check for "ClassicGroup" command in my callback for "CommandWIllStart"
and if "ClassicGroup" is the command, I add more event handlers (CommandCancelled, CommandEnded and CommandFailed) to the MDIActiveDocument.
When "ClassicGroup" is Ended/Cancelled/Failed, I do my work accordingly in the CommandEnded/Cancelled/Failed callback and I also remove "-=" the CommandCancelled, CommandEnded and CommandFailed handlers from the MDIActiveDocument.
I have been under the impression that removing the unused handlers was to avoid memory issues during the session.
Do I really have to remove the document event handlers at all?
TIA
Examples I have seen by autodesk I can see how they might convey that.
The object that 'publishes' the events is the one that holds an reference to subscribers.
Subscribing to documents events will not cause the document object to live any longer or not get GC.
The document object will keep your class that hold the subscribed handlers from being collected until it is collected.
And since the class that holds the subscribed handlers is static and lives until the App closes,
what happens if I close my app before autocad closes?
Will I get a Kaboom, because the documents are still alive?
Would'nt Autocad have to be closing if your app is closing?
I'm not sure what you mean by 'close my app'.
private static PartMonitor instance = null;
public static PartMonitor Instance
{
get
{
if (instance == null)
{
instance = new PartMonitor();
}
return instance;
}
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
e.Cancel = true;
Visible = false;
}
////
public class PartMonitor1
{
[CommandMethod("Partmon", CommandFlags.Session)]
public static void ShowModelessDialog()
{
Autodesk.AutoCAD.ApplicationServices.Application.ShowModelessDialog(PartMonitor.Instance);
}
}
Yes, if they have overhead. The Command-related events can have overhead especially when commands are being scripted at a high-frequency by other customization, namely LISP.
The way you're doing it with the Command events is IMO, the right way to do it, since you don't want your event handlers called when every command is ended/cancelled.