Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

What is the preferred and/or most efficient method for detecting a change in the table of contents in ArcMap using ArcObjects? My current implementation relies on a dictionary with the names and indices of the layers.

public static Dictionary<string, int> layerEnumerator()
{
    IEnumLayer enumLayer = ArcMap.Document.FocusMap.get_Layers(null, false);
    enumLayer.Reset();
    ILayer layer = enumLayer.Next();
    Dictionary<string, int> layerDictionary = new Dictionary<string, int>();
    int lyrNdx = 0;
    while (!(layer == null))
    {
        if (!(layerDictionary.ContainsKey(layer.Name)))
        {
          layerDictionary.Add(layer.Name, lyrNdx);
        }
        lyrNdx++;
        layer = enumLayer.Next();
    }

        return layerDictionary;
    }

Generally I will call this when I need to find the position of a specific layer in the ToC. Is there a more efficient means of gathering what's in the ToC at any given time (such as an event for ToC Contents Changed)? The solution I've provided above seems inadequate and inefficient at best.

share|improve this question
add comment

2 Answers

up vote 3 down vote accepted

There are a number of events you can subscribe to in order to do something when the map's contents change, items are added, reordered or removed, the maps collection changes, the map document is opened, saved, or closed, etc.

One thing that tripped me up was when I was trying to determine when a broken-data-source feature class's data source changes, which led me to use IActiveViewEvents.ContentsChanged as a general purpose catch-all. It will tell you when something changes, just not what changed. I am not sure if it will catch when things are added, removed or reordered in the TOC, since there are explicit events for those, so be sure to test.

Depending on what you are trying to do and whether you need to keep track of certain things, you could continue using a dictionary and just update it/run through your logic whenever those events fire. I would consider using a layer object's HashCode as a key instead of Name though since Name is not guaranteed to be unique and is actually liable to be changed by the user. Unfortunately ArcObjects provides no persistent unique layer identifier (in desktop at least, I think on the server side they do), so HashCode is about the best you can do.

You'll need to wire up multiple events starting with Open/NewDocument events in which you wire up your active view event handler(s) -- one for the FocusMap and one for the PageLayout as they receive different events -- as whenever the current document or active data frame is changed existing active view event handlers will become invalid. To be safe add one for CloseDocument where you unwire your active view event handlers.

Check the samples for examples of these as it can be tricky to get right.

share|improve this answer
    
Great, thanks. I'll probably keep my existing method to grab the layer by an identifier (probably changing it to the HashCode), but being able to fire this in a more controlled event like this is definitely what I needed. –  AHigh Aug 30 '12 at 22:25
    
Do NOT use an object's hash code as an identifier. There is no guarantee that two different objects's GetHashCode returns different values. –  Petr Krebs Aug 31 '12 at 10:27
    
Better than name, what else would you suggest? A combination of properties? Also I have never had a collision when using it with layer objects. –  blah238 Aug 31 '12 at 10:32
    
You can always keep the actual layer references, provided you keep track of changes in the document propely. –  Petr Krebs Sep 1 '12 at 14:22
1  
As a side note, I've seen people getting burnt by this exact (and very common, mind you) misconception about hash codes more than once. They are one of the core concepts in .NET and are designed for hashing purposes and nothing else. This very vital piece of information is clearly stated in the Remarks –  Petr Krebs Sep 1 '12 at 14:26
show 1 more comment

You can use the IActiveViewEvents handler to determine when an item is added or removed from the TOC. In this example clsPropSet.Document is an IMxDocument ref and clsPropSet.Map is an IMap ref.

    private static IDocumentEvents_CloseDocumentEventHandler cdHandler;
    private static IActiveViewEvents_ItemAddedEventHandler iaHandler;
    private static IActiveViewEvents_ItemDeletedEventHandler idHandler;

    internal void SetupEvents()
    {
        //HANDLE THE FOLLOWING EVENTS
        cdHandler = new IDocumentEvents_CloseDocumentEventHandler(OnDocClose);
        ((IDocumentEvents_Event)clsPropSet.Document).CloseDocument += cdHandler;
        iaHandler = new IActiveViewEvents_ItemAddedEventHandler(OnActiveViewEventsItemAdded);
        ((IActiveViewEvents_Event)clsPropSet.Map).ItemAdded += iaHandler;
        idHandler = new IActiveViewEvents_ItemDeletedEventHandler(OnActiveViewEventsItemDeleted);
        ((IActiveViewEvents_Event)clsPropSet.Map).ItemDeleted += idHandler;
    }

    internal void UnloadEvents()
    {
        ((IDocumentEvents_Event)clsPropSet.Document).CloseDocument -= cdHandler;
        ((IActiveViewEvents_Event)clsPropSet.Map).ItemAdded -= iaHandler;
        ((IActiveViewEvents_Event)clsPropSet.Map).ItemDeleted -= idHandler;
    }

    private void OnDocClose()
    {
        //DO SOMETHING WHEN DOCUMENT CLOSES
    }

    private void OnActiveViewEventsItemAdded(object Item)
    {
        //DO SOMETHING WHEN ITEM ADDED
    }

    private void OnActiveViewEventsItemDeleted(object Item)
    {
        //DO SOMETHING WHEN ITEM REMOVED
    }
share|improve this answer
    
This source snippet is really useful for implementing what I need to do. I'm going to accept the answer @blah238 provided as it seems to capture the solution in a more generalized sense. –  AHigh Aug 30 '12 at 22:26
    
That's cool. I figured you'd find the ContentsChanged event if you looked up the documentation for IActiveViewEvents. I just copy/pasted the above from one of my solutions. In this case I was only watching for items being added or removed. –  Rich Wawrzonek Aug 30 '12 at 23:00
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.