Author Topic: Object Events  (Read 12665 times)

0 Members and 2 Guests are viewing this topic.

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: 4094
  • 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.