DON'TS:

  • Don't dublicate subscriber / consumer logic of event.add/remove by introducing your own interfaces simulating it.

Instead of introducing the following code in the framework:

namespace DelftTools.Gui
{
    public interface IGuiObjectSelectionPublisher
    {
        /// <summary>
        /// Gets or sets current selected object(s). 
        /// Visibility of the menus, toolbars and other controls should be updated when selected object is changed.
        /// Default implementation will also show it in the PropertyGrid.
        /// </summary>
        object Selection { get; set; }

        /// <summary>
        /// Fired when user changes selection by clicking on it or by setting it using Selection property.
        /// </summary>
        event EventHandler SelectionChanged;
    }
}

namespace DelftTools.Gui
{
    public interface IGuiObjectSelectionSubscriber
    {
        ///<summary>
        /// Subscribes the action to the publisher
        ///</summary>
        ///<param name="action">The action to subscribe</param>
        void SubscribeAction(Action<object> action);

        ///<summary>
        /// Unsubscribes the action from the publisher
        ///</summary>
        ///<param name="action">The action to unsubscribe</param>
        void UnsubscribeAction(Action<object> action);

    }
}

namespace DelftTools.Gui
{
    ///<summary>
    /// This class is part of a more loosly coupled publisher subscriber pattern where 
    /// IGui is the publisher. Subscribers can be used anywhere in the application 
    /// Most of time this will be the plugins or view presenters that will listen to 
    /// selected object changes
    ///</summary>
    public class GuiObjectSelectionSubscriber : IGuiObjectSelectionSubscriber
    {
        private readonly IGuiObjectSelectionPublisher publisher;
        private readonly HashSet<Action<object>> actions;

        ///<summary>
        /// Constructs the subscriber
        ///</summary>
        ///<param name="publisher">The publisher to observe changes from</param>
        public GuiObjectSelectionSubscriber(IGuiObjectSelectionPublisher publisher)
        {
            this.publisher = publisher;
            this.actions = new HashSet<Action<object>>();
        }

        ///<summary>
        /// Subscribes the action to the publisher
        ///</summary>
        ///<param name="action">The action to subscribe</param>
        public void SubscribeAction(Action<object> action)
        {
            actions.Add(action);

            if (actions.Count == 1)
            {
                publisher.SelectionChanged -= OnSelectedObjectChanged;
                publisher.SelectionChanged += OnSelectedObjectChanged;
            }
        }

        ///<summary>
        /// Unsubscribes the action from the publisher
        ///</summary>
        ///<param name="action">The action to unsubscribe</param>
        public void UnsubscribeAction(Action<object> action)
        {
            actions.Remove(action);

            if(actions.Count == 0)
                publisher.SelectionChanged -= OnSelectedObjectChanged;
        }
       
        /// <summary>
        /// Handles the internal IGui SelectionChanged events. The handler 
        /// will use the selected object as the argument value in the action
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnSelectedObjectChanged(object sender, EventArgs args)
        {
            foreach (var action in actions)
            {
                action(publisher.Selection);
            }
        }   
     
        /// <summary>
        /// Remove all referencences
        /// </summary>
        ~GuiObjectSelectionSubscriber()
        {
            if (actions.Count == 0) return;

            actions.Clear();
            publisher.SelectionChanged -= OnSelectedObjectChanged;
        }
    }
}

... and using it like this (in every presenter):

namespace DelftShell.Plugins.NetworkEditor.Forms.CompositeStructureView
{
    public class CompositeStructureViewPresenter : Presenter<ICompositeStructureView>, ICanvasEditor
    {
        private readonly IGuiObjectSelectionSubscriber subscription; 
        private readonly Action<object> selectObjectInViewsAction;

        ...        

        public CompositeStructureViewPresenter(IGuiObjectSelectionSubscriber subscription)
        {
            selectObjectInViewsAction = (e =>
                                             {
                                                 var structure = e as IStructure;
                                                 if (structure != null)
                                                    SelectObjectInViews(null, structure);
                                             });
    
            this.subscription = subscription;
            this.subscription.SubscribeAction(selectObjectInViewsAction);
        }
        
        ...
}

NetworkEditorViewProvider.cs:

                var presenter = new CompositeStructureViewPresenter(new GuiObjectSelectionSubscriber(Gui));
                presenter.ViewProvider = Gui.ViewProvider;

Just use:

NetworkEditorViewProvider.cs:

                var presenter = new CompositeStructureViewPresenter(new GuiObjectSelectionSubscriber(Gui));
                presenter.ViewProvider = Gui.ViewProvider;

                Gui.SelectionChanged += new WeakEventHandler<SelectedItemChangedEventArgs>((sender, args) =>
                {
                    if (args.Item is IStructure)
                    {
                        var structure = (IStructure)args.Item;
                        compositeStructureViewPresenter.SelectObjectInViews(null, structure);
                    }
                });
                

The code is much shorter and completely replaces all the logic above without introducing unnecessary complexity to the framework. One more good thing is that it is better to keep things loosely coupled instead of putting gui selection logic into every specific presenter, just having it in the presenter/view factory class is good enough. There is no need for a CompositeStructureViewPresenter to depend directly on a framework selection logic IGuiObjectSelectionSubscriber, it will be asked when to select things. Control over when it will be asked still stays in a plugin (view provider).

Keep in mind that DelftShellGui and NetworkEditorViewProvider are in this case linked in a weak way so once a view will disappear - subscribtion will be garbage collected.

I really hope that Microsoft will add it as default feature in .NET 4.0 or SP1, something like += weak delegate {...}. You can vote here to get it implemented in the next .NET.

By the way, instead of redirecting selection to a presenter it might be better to make it work in a view (but probably then we need to introduce a Iview.Selection mechanism first):

                Gui.SelectionChanged += new WeakHandler<SelectedItemChangedEventArgs>((sender, args) =>
                {
                    if (args.Item is IStructure)
                    {
                        compositeStructureView.SelectedStructure = (IStructure)args.Item;
                    }
                });

References: