Tuesday, September 11, 2007
Posted on Tuesday, September 11, 2007 4:00:23 AM (Mountain Daylight Time, UTC-06:00)  Comments [3] | 
Categories: .NET | ArcGIS Devt | ArcMap

A couple of times I've seen people asking about how to share information between forms/commands/tools etc. in ArcMap and I thought I'd create a simple example of how I've been doing it.

Problem

At a high level, the problem is pretty simple - one part of your application needs to know about the state of another part. One example could be a combo box tool control that allows the user to select an Active layer, and you have other tools/controls/forms etc that need to know what the active layer is, and need to be informed when the active layer changes.

Solution

This is an obvious case where custom events are going to come into play, since they are the standard way to provide notification of a state change to a set of "subscribing" or listening classes. In standard .NET applications, you'd create an event right on the actual source - in this case the tool control. However, since we are working in ArcMap, we have a slightly different situation. Since we (developers) do not control the load order of tools/commands etc in ArcMap, it's rather difficult to effectively sink up event handlers and sources if they are all located in your customization classes (ICommand/ITool etc). This is because we don't really know when either class is actually instantiated during the load process, and once it's instantiated, it's a pain to actually get a pointer to it.

The way to solve this is to route the events through something that's always loaded, and easily accessed - such as an application extension (IExtension).

Since application extensions (with the exception of JIT Extensions) are always loaded when ArcMap is starting up, we know it's always present. Regardless of when the event source tool / form / command is initialized, it will always be able to call a method on the extension, which then raises an event. The same it true for the classes handling the event - since the extension is loaded and accessible, they can wire up an event handler during their initialization, or any time after that.

ex-event1

While there are other ways to pass "state" around, using an event allows a whole set of classes to then subscribe to the event and be notified when changes occur.

ex-event2

Once you think about it, this is quite obvious, and it is a small scale mirror of much of the ArcObjects customization API - write a class, wire it into ArcMap, and have it respond to ArcMap events. Except in this case, it's our own custom event.

Sample

The sample is in C#, and is kept very simple, so the focus is on wiring the events.

Extension:

The extension has a public SetSelectedItem method, which in turn raises the SelectedItemChanged event.

Commands:

There are two commands - EventSourceCommand and EventSinkCommand. In the onCreate function, both commands get pointers to the event extension, and pass this into the IntializeUI methods on the forms. The Event Source Form has a simple combo box listing 5 fruits. In the OnChange event of the combo box, the form calls SetSelectedItem on the extension. The Extension raises the SelectedItemChanged event, to which the Event Sink Form has  subscribed, and a label is updated to show the currently selected fruit. Although it's a bit complex when you read it, this is very simple when you look at the code, and run it.

Since the sample is in C#, it uses event wiring involving delegates. If you are new to delegates, O'Reilly has a good article - Writing C# Custom Events, which I would recommend. The syntax is much simpler in VB.NET, and here's a CodeProject article on that. Of course you can use delegates in VB.NET if you want to, so for completeness sake, here's an article on events using delegates in VB.NET. Moving on...

To run the sample, download the code, compile it, and run ArcMap. You should get a pop-up box as ArcMap is loading which tells you that the Event Extension is loading. Once ArcMap is started, open up the Customize dialog, and look in the DaBo.Samples category.

event-tools

Drag the Event Sink and Event Source command onto a toolbar, and then run them. Both commands open forms. Use the pulldown on the Event Source Form to update the label on the Event Sink Form.

event-sample

When you dig into the code you'll see that I've used the out of the box templates for the commands and extension. The only other "cool" thing I threw in was the ArcMapWindow class I wrote about previously.

Have fun wiring up your events!

Download Sample

Tuesday, September 11, 2007 11:20:12 AM (Mountain Daylight Time, UTC-06:00)
Hey Dave -

Good article! Something I've never quite determined though is whether it is necessary for a subscriber to remove (unwire) the event when it shuts down (or disposes).

The codeproject example mentions the RemoveHandler, but I've never seen a single ESRI example that unwires events at shutdown (or via dispose).

Would you happen to know if unwiring is necessary, seems like without it there is a risk of circular references. I've got an Arcmap extension that causes arcmap to hang on exit. It doesn't happen on the deployment machine, so I haven't worried about it. I'm thinking I'll try a dispose approach as outlined here, (when it becomes a priority):
http://blogs.msdn.com/ploeh/archive/2006/08/10/HowToDisposeMembersFromForms.aspx



Regards, Kirk
Wednesday, September 12, 2007 9:19:08 AM (Mountain Daylight Time, UTC-06:00)
Thank you very much. These are the types of things I have trouble finding about developing in ESRI .net. Please keep writing about these "tips and tricks" they are very helpful.

Blizzardice
blizzardice
Saturday, September 15, 2007 11:10:19 PM (Mountain Daylight Time, UTC-06:00)
@Kirk

First off Thanks! Re: Removing Handlers - we've got all kinds of events on our extensions and we don't remove any handlers, and all seems to be working just fine. I think that's mainly used in cases where you want to stop listening to events, but not dispose of the class.

Cheers,

Dave
Comments are closed.