IActiveViewEvents SelectionChanged stops working after IDocumentEvents OpenDocument

1324
2
Jump to solution
04-01-2014 10:40 AM
KeithWeber1
New Contributor III
I am developing an extension to ArcMap and trying to listen to the SelectionChanged event. There are several classes in my code that want to be notified when the selection changes so I was trying to write a simple wrapper to handle setting up the event handlers. The code works when ArcMap starts up and for the NewDocument event, but as soon as the OpenDocument event occurs, nothing works until the application is restarted. No exceptions (that I can tell) are thrown through the entire process. Any help would be greatly appreciated.

Update: I also noticed that after the OpenDocument event occurs, All of the IDocumentEvents stop working.



Imports ESRI.ArcGIS.ArcMapUI Imports ESRI.ArcGIS.Carto Imports ESRI.ArcGIS.Framework Imports System.Windows.Forms  Public Class MapEvents     Implements IDisposable      Private Property _application As IApplication     Private Property _activeViewListening As Boolean      Public Event ActiveViewSelectionChanged()     Public Event ActiveViewItemDeleted(ByRef Item As Object)      Public Sub New(ByRef application As IApplication)         _application = application         Dim mxDocument As IMxDocument = application.Document         StartListening(mxDocument)         _activeViewListening = False     End Sub      Public Sub Dispose() Implements IDisposable.Dispose         Dim mxDocument As IMxDocument = _application.Document         StopListening(mxDocument)         StopListening(mxDocument.ActiveView)         _application = Nothing     End Sub       Private Sub StartListening(ByRef mxDocument As IMxDocument)         If (mxDocument Is Nothing) Then             Return         End If         Dim documentEvents As IDocumentEvents_Event = mxDocument         AddHandler documentEvents.NewDocument, AddressOf OnNewDocument         AddHandler documentEvents.OpenDocument, AddressOf OnOpenDocument         AddHandler documentEvents.BeforeCloseDocument, AddressOf OnBeforeCloseDocument     End Sub      Private Sub StopListening(ByRef mxDocument As IMxDocument)         If (mxDocument Is Nothing) Then             Return         End If         Dim documentEvents As IDocumentEvents_Event = mxDocument         RemoveHandler documentEvents.NewDocument, AddressOf OnNewDocument         RemoveHandler documentEvents.OpenDocument, AddressOf OnOpenDocument         RemoveHandler documentEvents.BeforeCloseDocument, AddressOf OnBeforeCloseDocument     End Sub      Private Sub StartListening(ByRef activeView As IActiveView)         If (_activeViewListening) Then             Return         End If         If (activeView Is Nothing) Then             Return         End If         Dim activeViewEvents As IActiveViewEvents_Event = activeView         AddHandler activeViewEvents.SelectionChanged, AddressOf OnActiveViewSelectionChanged         AddHandler activeViewEvents.ItemDeleted, AddressOf OnActiveViewItemDeleted         _activeViewListening = True         MessageBox.Show("Active View Listening = True")     End Sub      Private Sub StopListening(ByRef activeView As IActiveView)         If (activeView Is Nothing) Then             Return         End If         Dim activeViewEvents As IActiveViewEvents_Event = activeView         RemoveHandler activeViewEvents.SelectionChanged, AddressOf OnActiveViewSelectionChanged         RemoveHandler activeViewEvents.ItemDeleted, AddressOf OnActiveViewItemDeleted         _activeViewListening = False         MessageBox.Show("Active View Listening = False")     End Sub      Private Sub StartActiveViewListening()         Dim mxDocument As IMxDocument = _application.Document         StartListening(mxDocument.ActiveView)     End Sub      Private Sub OnNewDocument()         Try             StartActiveViewListening()         Catch ex As Exception             Dim errorForm As ErrorForm = New ErrorForm(ex)             errorForm.Show()         End Try     End Sub      Private Sub OnOpenDocument()         Try             StartActiveViewListening()         Catch ex As Exception             Dim errorForm As ErrorForm = New ErrorForm(ex)             errorForm.Show()         End Try     End Sub       Private Sub OnActiveViewSelectionChanged()         Try             MessageBox.Show("Active View Selection Changed.")             RaiseEvent ActiveViewSelectionChanged()         Catch ex As Exception             Dim errorForm As ErrorForm = New ErrorForm(ex)             errorForm.Show()         End Try     End Sub      Private Sub OnActiveViewItemDeleted(Item As Object)         Try             MessageBox.Show("Active View Item Deleted.")             RaiseEvent ActiveViewItemDeleted(Item)         Catch ex As Exception             Dim errorForm As ErrorForm = New ErrorForm(ex)             errorForm.Show()         End Try     End Sub      Private Function OnBeforeCloseDocument() As Boolean         Try             Dim mxDocument As IMxDocument = _application.Document             StopListening(mxDocument.ActiveView)         Catch ex As Exception             Dim errorForm As ErrorForm = New ErrorForm(ex)             errorForm.Show()         End Try         Return False     End Function   End Class
0 Kudos
1 Solution

Accepted Solutions
NeilClemmons
Regular Contributor III
Usually what is happening when someone asks this question is they are not rewiring the events when the document changes or they are not rewiring the events correctly.  When the document changes, you should unwire all event listeners that were added using the previous document and then rewire those event handlers using the new document.  If you don't, then your event handlers are still listening to the previous document's events and will cease to fire since that document is no longer the current document.  Here's an example of how to do it correctly:

Declarations
    Private m_documentEvents As IDocumentEvents_Event     Private m_activeViewEvents As IActiveViewEvents_Event


Extension Startup routine where events are initially wired
        Dim mxDocument As IMxDocument = DirectCast(ESRIContainer.Instance.Application.Document, IMxDocument)         m_documentEvents = DirectCast(mxDocument, IDocumentEvents_Event)         m_activeViewEvents = DirectCast(mxDocument.FocusMap, IActiveViewEvents_Event)          AddHandler m_documentEvents.NewDocument, AddressOf NewDocument         AddHandler m_documentEvents.OpenDocument, AddressOf OpenDocument         AddHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         AddHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted


Events are unwired then rewired inside the NewDocument and OpenDocument event handlers
    Private Sub NewDocument()         RemoveHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         RemoveHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          Dim mxDocument As IMxDocument = DirectCast(ESRIContainer.Instance.Application.Document, IMxDocument)         m_activeViewEvents = DirectCast(mxDocument.FocusMap, IActiveViewEvents_Event)          AddHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         AddHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          LoadOutputMapItems()     End Sub      Private Sub OpenDocument()         RemoveHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         RemoveHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          Dim mxDocument As IMxDocument = DirectCast(ESRIContainer.Instance.Application.Document, IMxDocument)         m_activeViewEvents = DirectCast(mxDocument.FocusMap, IActiveViewEvents_Event)          AddHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         AddHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          LoadOutputMapItems()     End Sub


I didn't include the code for the ItemAdded and ItemDeleted event handlers since it's not important, but these would be the events in my example that would stop firing if I didn't rewire them.  Also, technically the extension's Shutdown routine should do a final unwiring of the events but it really doesn't matter since ArcMap is closing at that point.

One important thing to note is that the IMxDocument reference is taken from the IApplication reference each time it is needed.  If you set a global document reference then you run the risk of it also pointing to the previous document and not the document currently loaded.  In my example, I'm using a singleton object class that we use in all of our applications that handles this but you can use the application hook passed to your extension just as easily.

View solution in original post

0 Kudos
2 Replies
NeilClemmons
Regular Contributor III
Usually what is happening when someone asks this question is they are not rewiring the events when the document changes or they are not rewiring the events correctly.  When the document changes, you should unwire all event listeners that were added using the previous document and then rewire those event handlers using the new document.  If you don't, then your event handlers are still listening to the previous document's events and will cease to fire since that document is no longer the current document.  Here's an example of how to do it correctly:

Declarations
    Private m_documentEvents As IDocumentEvents_Event     Private m_activeViewEvents As IActiveViewEvents_Event


Extension Startup routine where events are initially wired
        Dim mxDocument As IMxDocument = DirectCast(ESRIContainer.Instance.Application.Document, IMxDocument)         m_documentEvents = DirectCast(mxDocument, IDocumentEvents_Event)         m_activeViewEvents = DirectCast(mxDocument.FocusMap, IActiveViewEvents_Event)          AddHandler m_documentEvents.NewDocument, AddressOf NewDocument         AddHandler m_documentEvents.OpenDocument, AddressOf OpenDocument         AddHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         AddHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted


Events are unwired then rewired inside the NewDocument and OpenDocument event handlers
    Private Sub NewDocument()         RemoveHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         RemoveHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          Dim mxDocument As IMxDocument = DirectCast(ESRIContainer.Instance.Application.Document, IMxDocument)         m_activeViewEvents = DirectCast(mxDocument.FocusMap, IActiveViewEvents_Event)          AddHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         AddHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          LoadOutputMapItems()     End Sub      Private Sub OpenDocument()         RemoveHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         RemoveHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          Dim mxDocument As IMxDocument = DirectCast(ESRIContainer.Instance.Application.Document, IMxDocument)         m_activeViewEvents = DirectCast(mxDocument.FocusMap, IActiveViewEvents_Event)          AddHandler m_activeViewEvents.ItemAdded, AddressOf ItemAdded         AddHandler m_activeViewEvents.ItemDeleted, AddressOf ItemDeleted          LoadOutputMapItems()     End Sub


I didn't include the code for the ItemAdded and ItemDeleted event handlers since it's not important, but these would be the events in my example that would stop firing if I didn't rewire them.  Also, technically the extension's Shutdown routine should do a final unwiring of the events but it really doesn't matter since ArcMap is closing at that point.

One important thing to note is that the IMxDocument reference is taken from the IApplication reference each time it is needed.  If you set a global document reference then you run the risk of it also pointing to the previous document and not the document currently loaded.  In my example, I'm using a singleton object class that we use in all of our applications that handles this but you can use the application hook passed to your extension just as easily.
0 Kudos
KeithWeber1
New Contributor III
Neil,

Thanks! I am a little confused as to why my code was failing because I thought I was removing the IActiveView event handlers correctly by listening to the IDocumentEvents::BeforeCloseDocument event then adding them back during the IDocumentEvents::NewDocument and IDocumentEvents::OpenDocument. However, using the method that you illustrated in your sample code (storing a local instance of the IActiveViewEvents_Event and IDocumentEvents_Event and replacing the handlers within the NewDocument and OpenDocument events) has resolved the problem. Thanks again!!!
0 Kudos