Select to view content in your preferred language

SketchCompletedEvent firing only sometimes

682
6
Jump to solution
08-29-2023 11:19 AM
ViktorSafar
Occasional Contributor II

I have a VM controlling a custom dock pane. The VM at some point starts vertex editing via esri_editing_EditVerticesModifyFeature plugin wrapper.

Just before that I set up an event handler for when the vertex editing bit is ended by the user - via SketchCompletedEvent or SketchCanceledEvent

 

 

SubscriptionToken canceledToken = null;
SubscriptionToken completedToken = null;

canceledToken = ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Subscribe(async ags =>
{
	await HandleCombinedSketchDone(canceledToken, completedToken);
});
completedToken = ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Subscribe(async ags =>
{
	await HandleCombinedSketchDone(canceledToken, completedToken);
});

await FrameworkApplication.SetCurrentToolAsync(null);
var editCommand = FrameworkApplication.GetPlugInWrapper("esri_editing_EditVerticesModifyFeature") as ICommand;
if (editCommand != null && editCommand.CanExecute(null))
{
	editCommand.Execute(null);
}

 

 

  

These events appear to fire super rarely. I have not been able to find a pattern. Can anyone shed some light on this or am I doing something wrong?

In the event handler method HandleCombinedSketchDone I unsubsribe from the events and activate the current pane:

 

 

private async Task HandleCombinedSketchDone(SubscriptionToken canceledToken, SubscriptionToken completedToken)
{
	ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Unsubscribe(canceledToken);
	ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Unsubscribe(completedToken);
	MapView.Active.Map.ClearSelection();
	await Application.Current.Dispatcher.Invoke(() =>
	{
		this.Activate();
	});
}

 

 

  

The buttons that should (and sometimes do) fire those events are the Finish/Cancel buttons on the Modify Features pane:

ViktorSafar_0-1693333103522.png

 

0 Kudos
1 Solution

Accepted Solutions
John_Jones
New Contributor III

Viktor,

Looking at your repro case (thanks much) I do see what you are seeing and it appears that possibly since your event handlers (lambdas) are capturing the subscription token (local variables) and the function returns the closures for your event handlers are being marked eligible for garbage collection (nothing is keeping them alive) so sometimes the collector doesn't run and your event handler still gets called but most of the time (at least in my testing with the debugger attached) the garbage collector runs and when the event is fired your event handler has been torn down as it is observed that it isn't alive anymore (the event handling mechanism in Pro by default keeps only a weak pointer to the event handler).  To bypass this the easiest change is to instruct the eventing mechanism in Pro to keep your event handler alive by setting the "keepSubscriberAlive" parameter to Subscribe to true.  Two alternate selections to help bypass this would be to make your event handlers members of your DockPane class (which wont be disposed promptly since the Pro Framework keeps a handle to it) and to hold your SubscriptionTokens in member variables of your Dock pane class (perhaps disconnecting from events when your dock pane is closed...)  Hope this helps get you back on track.

        canceledToken = ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Subscribe(async ags =>
        {
          ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Unsubscribe(canceledToken);
          ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Unsubscribe(completedToken);
          MessageBox.Show("Hit from SketchCanceledEvent");
        }, true);
        completedToken = ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Subscribe(async ags =>
        {
          ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Unsubscribe(canceledToken);
          ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Unsubscribe(completedToken);
          MessageBox.Show("Hit from SketchCompletedEvent");
        }, true);

 

View solution in original post

0 Kudos
6 Replies
RichardDaniels
Regular Contributor
To try to find out what is happening you may want to add this to the task that you want to await, If you place a try/catch block around the ENTIRE body of your event handler, then you can prevent any exceptions from bubbling up across that async void boundary.
try
{
await SomeTaskYouWantToAwait();
}
catch (Exception ex)
{
// TODO: put your exception handling stuff here

Console.WriteLine($"[TryAsync Error Callback] Our exception handler caught: {ex}"));


}

0 Kudos
ViktorSafar
Occasional Contributor II

The handler is not a problem.. The event is not fired, a breakpoint on line 9 in the 1st code block is never/rarely hit.

0 Kudos
John_Jones
New Contributor III

Viktor,

I am seeing some cases where these events are not fired, but basic cases of user clicking Finish or Cancel are appearing to work when I look at them.  Cases where I don't see either event fired are clearing the selection, deactivating the tool or changing the sketch back into selection mode. (clicking on the "Select a feature" button in the Edit vertices pane).  

I would suggest making your SubscriptionToken objects members of your dock pane class (not local variables) and to be careful not to subscribe if you are already subscribed (don't subscribe twice) and to force unsubscribing if your dock pane is closed (or other interesting things happen, perhaps even if the active tool is changed to another tool).  To make the code clearer (doesn't appear to be involved in what you are seeing) note that the event handlers are not asynchronous.  That is the code firing these events doesn't await the Task you are returning in your event handlers (they are declared as Action<SketchCompletedEventArgs> not Func<SketchCompletedEventArgs,Task>) what this means is that when your event handler schedules the dock pane activation on the UI thread) the code won't wait for that to happen but will immediately proceed on the background thread.  

I would appreciate a full example add-in (with source) to examine to see what isn't working for you.  I don't need your complete code just enough that reproduces the problem (ideally with repro steps.)  I'll reach out and try to help more directly and report back with information generally helpful to the community.

Thanks

John

0 Kudos
John_Jones
New Contributor III

Viktor,

Looking at your repro case (thanks much) I do see what you are seeing and it appears that possibly since your event handlers (lambdas) are capturing the subscription token (local variables) and the function returns the closures for your event handlers are being marked eligible for garbage collection (nothing is keeping them alive) so sometimes the collector doesn't run and your event handler still gets called but most of the time (at least in my testing with the debugger attached) the garbage collector runs and when the event is fired your event handler has been torn down as it is observed that it isn't alive anymore (the event handling mechanism in Pro by default keeps only a weak pointer to the event handler).  To bypass this the easiest change is to instruct the eventing mechanism in Pro to keep your event handler alive by setting the "keepSubscriberAlive" parameter to Subscribe to true.  Two alternate selections to help bypass this would be to make your event handlers members of your DockPane class (which wont be disposed promptly since the Pro Framework keeps a handle to it) and to hold your SubscriptionTokens in member variables of your Dock pane class (perhaps disconnecting from events when your dock pane is closed...)  Hope this helps get you back on track.

        canceledToken = ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Subscribe(async ags =>
        {
          ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Unsubscribe(canceledToken);
          ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Unsubscribe(completedToken);
          MessageBox.Show("Hit from SketchCanceledEvent");
        }, true);
        completedToken = ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Subscribe(async ags =>
        {
          ArcGIS.Desktop.Mapping.Events.SketchCanceledEvent.Unsubscribe(canceledToken);
          ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Unsubscribe(completedToken);
          MessageBox.Show("Hit from SketchCompletedEvent");
        }, true);

 

0 Kudos
John_Jones
New Contributor III

weak pointer -> weak reference (wrong language 🙂 )

0 Kudos
ViktorSafar
Occasional Contributor II

Thanks John, that actually makes perfect sense.

0 Kudos