Map.Layers.LayerAdded and Map.Layers.LayerRemoved Events

Jan 20, 2014 at 7:49 PM
I had setup my application to watch for these two events, so that on changes made the interface updates with the changes made to the map, I thought this was working fine but have come to discover that if a user moves a layer inside of the Legend both the layer Remove and Add events fire. The Add event always has a null layer value and the actual count of the layers on the map doesn't seem to change but it messes up my interface changes. Is this normal? SHould I be halting these events somehow? Should I be using events from another object? Ideas here would be wonderful.

Jan 21, 2014 at 3:36 AM
Hi, i think it is a bug that Add event always has a null layer in this case. It should contains a reference to moved layer.
Jan 21, 2014 at 5:59 PM
Ok, I have a temporary workaround, but I'll see if I can spend some time tracking through the event and find the bug.
Jan 23, 2014 at 10:21 AM
Anyway, in case when layer is moving inside legend OnLayerAdded event should have a link to layer. Please let me know, if this change will do usage of these events a bit clear in your application.
Mar 19, 2014 at 4:47 PM
Edited Mar 19, 2014 at 5:03 PM
So, I finally got a chance to go back in investigate this issue. It basically comes down to following:

In Legend.cs in DotSpatial.Controls.Legend on lines 702 and 723
699 ILegendItem potentialParent = _dragTarget.Item.GetValidContainerFor(_dragItem.Item);
700 if (potentialParent != null)
701 {
702     potentialParent.ParentMapFrame().SuspendEvents(); 
704     // The target must be a group, and the item must be a layer.
705     ILayer lyr = _dragItem.Item as ILayer;
706     if (lyr != null)
707     {
708         IGroup grp = _dragItem.Item.GetParentItem() as IGroup;
709         lyr.LockDispose();
710         //when original location is inside group, remove layer from the group.
711         if (grp != null) grp.Remove(lyr);
712         int index = _dragTarget.Item.InsertIndex(_dragItem.Item);
713         if (index == -1) index = 0;
714         grp = potentialParent as IGroup;
715         if (grp != null)
716         {
717             grp.Insert(index, lyr);
718             //when the target is a group, assign the parent item.
719             lyr.SetParentItem(grp);
720         }
721         lyr.UnlockDispose();
722     }
723     potentialParent.ParentMapFrame().ResumeEvents();
724     OnOrderChanged();
725     potentialParent.ParentMapFrame().Invalidate();
726 }
When the layer is moved inside of the map legend the events are being suspended and then resumed.
As this event trickles down through the mapframe we eventually end up at ChangedEventList.cs inside DotSpatial.Data at line 323
323 protected virtual void OnListChanged()
324 {
325     if (EventsSuspended == false)
326     {
327         if (ItemChanged != null)
328         {
329             // activate this remarked code to test if the handlers are getting copied somewhere.
330             //int count = ItemChanged.GetInvocationList().Length;
331             //if (count > 1) Debug.WriteLine(this + " has " + count + " item changed handlers.");
332             ItemChanged(this, EventArgs.Empty);
333         }
334     }
335     else
336     {
337         _hasChanged = true;
338     }
339 }
So basically because the event is suspended and then later resumed the ItemChanged event that is fired with the LayerEventArgs is ignored. Later after the events are resumed line 332 fires which is including an EventArgs.Empty, not the original LayerEventArgs we need.

I commented out lines 702 and 723 (the suspend and resume events on the legend itself) recompiled DotSpatial.Controls and it works fine in this manner. However, I'm sure there is a reason events were being suspended to start with. Unfortunately, I'm not as familiar with the DotSpatial code to tell if it is a bad idea to remove these event suspensions. I'd be happy to submit a patch, but wanted to clarify if this is a suitable solution or if this will have a larger impact elsewhere.

Mar 24, 2014 at 9:00 AM
Commenting suspending\resuming events it not good solution. In fact we not need LayerAdded/LayerRemoved events when we just reordering layers in legend. There is a separate event to handle this - Legend.OrderChanged.
I've fixed this behavior - now these both events are not raised in this case. Please try commit 74090.
Marked as answer by mogikanin on 3/28/2014 at 3:52 AM
Mar 25, 2014 at 2:42 PM
thanks mogikanin,

checked out the latest commit, and seems to be running great now.