View Full Version : Unregistering Event Handlers (Design Question)
2009-08-27, 01:51 PM
This is more an overall design best-practices for AutoCAD/C#.NET than a specific coding problem. I'm still at the (re)design stage of my application.
Suppose you're registering different events at the Application, Document Collection, and Document levels. Let's further suppose the Application and Document Collection event handlers get constructed/registered when the application is started. Obviously I want those objects to persist as long as the application is open (or until I'm done with them, at least).
The Document event handler could be constructed/registered by the Document Collection event handler at the DocumentCreated event and disposed of at DocumentDestroyed... but should it? One could also have the Document-level event handler constructed by the Document Collection event handler at the DocumentBecameCurrent event, and then unregistered and disposed of at DocumentToBeDeactivated.
As far as I can tell, the decision whether to do this is dependent on whether the resources for construction/destruction of the Document event handler outweigh the resources required to maintain the Document event handler when the document isn't active, right? But if I'm responding only to CommandWillStart or BeginDocumentClose events, then there won't be any events that I need to respond to while the document is inactive, right? Or in other words, what Document events can be triggered while the inactive?
What I'm worried about is the all-too-common-at-my-firm situation where the user has 12 drawings open, and now I've got 12 Document event handler objects floating around. If the documents they are watching aren't active, are they using up much in the way of resources?
2009-09-22, 03:04 PM
Let me first thank you for such a thorough response, Tony.
As far as the overhead of maintaining document- and editor-level events on multiple open documents, I have found that it is not even worth mentioning, and provided that the handlers for the events do not have high overhead, I leave document- and editor-level handlers intact for all open documents, and add and remove them in handlers of the DocumentCreated and DocumentToBeDestroyed events, respctively.
In the time since I posted the OP, I've experimented with this myself and come to the same conclusion. It seems like there's a general design pattern here that's not specific to AutoCAD, but I'm admittedly lacking in the general application design background to say for sure. (I'm in the middle of transitioning to Career 2.0, as it were.)
There are obscure cases where events can fire on a document that is not active and on its Database, although they are very rare. For example, the document locking related events can fire when a document is not active. But, the command-related events and almost all Editor events will never fire on an inactive document, if that's what you want to know.
Yeah, I was worried there was some "gotcha" corner case I'd missed. Glad to know I'm not barking up the wrong tree.
One thing worth noting about handling Command events, is that many of the the coding examples on through-the-interaface relating to using Command events, do not serve as good examples of how to use them efficiently, because they needlessly handle all command-related events continuously, when it is only necessary to monitor the CommandWillStart event continuously, and the end-command related event handlers can be added/removed temporarily.
That seems obvious to me, now. But Kean doesn't seem to do this all the time and definitely warns against it(for example here, http://through-the-interface.typepad.com/through_the_interface/2008/08/rolling-back-th.html, which was an article I see you commented on). What's the motivation for Kean to register the end-command events "early" when he does it, then? I wasn't able to find an example. For purposes of my application it's not going to matter because I'm only working with CommandWillStart right now, but I'm curious as to your thoughts on that.
Here's an example that shows this pattern wrapped up a reusable base type that automates much of the 'grunt' work:
Thanks for this and the other example.
2009-09-24, 01:16 PM
Tony, if you don't mind, I have a follow up question on the CommandEventsSample that you posted. I've had a chance to dig through it in detail, and it looks like a more robust, generalized, and flexible solution than what I already have in place. And it makes adding a new command event reactor *very* easy.
Right now I have the chain of events I described in the OP: when the assembly is initialized, it constructs my document collection events monitor. On Doc_Created, the document events monitor gets created for that document and registers the CommandWillStart event handler. I also have a project-specific "policy file" that gets deserialized from XML, and that policy object gets stored in each document's UserData on Doc_Created. The various CommandWillStart reactors (and a few custom commands) rely on this policy object to control their behavior. (The policy files are intended to be reasonably human-readable for the users and customizable per project, if you're wondering why I'd jump through the XML hoop.)
Looking at your sample, I'm now trying to figure out whether I need the IDisposable interface to be inherited by my UserData-related classes. My understanding was that if you're dealing with managed code then you can let garbage collection clean up after you. On Doc_Destroyed, the UserData hash table associated with a document should be garbage collected, right? (Which then triggers GB on objects stored in it?) I think what's contributing to my confusion is that you have a couple DocData abstract classes that implement IDisposable and another that does not as far as I can tell (the comment on line 15-16 in DocData.cs seems to indicate otherwise, but I'm not seeing it). Where do you use one vs the other?
Obviously, per the license on this sample, I could pretty easily just drop those classes in and use this as-is with a minimum of fuss (this isn't for distribution). But obviously it would be better for my education if I understood why you've done it this way. =)
2009-09-28, 07:15 PM
I think that comment is old/stale, because DocData doesn't implement IDisposable, and you generally don't need to implement that interface,
unless you have some need to release unmanaged resources.
Ah, the comment may very well be the source of my confusion. I was having trouble figuring out why I couldn't find the IDisposable interface! And you also have the DisposableDocData class that does implement IDisposable, and as you say, you would only use that if whatever you were sticking into UserData was using unmanaged resources.
If you have to load some XML or other data associated with a document, you can do that in Attach(), or you can defer it until the first time the data is actually needed/accessed. I would choose the latter, mainly because
if my code doesn't need the data for some reason, I don't have to wait for
it to be loaded.
In this case I'd rather the external data already be handy when appropriate commands fire, rather than be constructed on-demand. The user is expecting n amount of time for a document load anyway, so adding the fetch of the XML and construction of that object isn't noticeable. But adding that time to the start of a command might be.
Anyways, at this point I already have my event handlers and UserData classes coded up and working, although without the extensibility your two solutions (DocData and CommandEvents). At this point I probably won't backtrack through to implement the more extensible solutions, but I'll certainly keep them in mind if I end up extending what I've got.
Thanks again for taking the time to share and discuss this.
Powered by vBulletin® Version 4.1.11 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.