MVP (Model View Presenter) – Supervising Controller.

Intro:

The Model-View-Control (MVC) was a breakthrough in its time and changed the way UI development was approached.  It provided a clean boundary between the core domain logic and the presentation logic.  Now, there are many descendants of the MVC pattern that accomplish this same goal.  One variation is the Model View Presenter (MVP) which has several different approaches, most of which differ in the amount of responsibility of the control.  We'll look at using the Supervising Controller which takes responsibility for the complex user interaction but tries to stay out of the way most of the time leaving the view to update itself through binding.  The Supervising Controller will step in when there is complex view logic.
If you are not familiar with the MVC pattern, check out my article here:  http://www.c-sharpcorner.com/UploadFile/rmcochran/MVC_intro12122005162329PM/MVC_intro.aspx

A pure MVC implementation keeps all UI logic in the view.  The Supervising Controller pattern varies a bit in that we allow the view and the model to interact through binding mechanisms but try to move any complex UI logic into the controller.  When the model changes the view is notified through the bindings and updates itself.  We can manually code some extra binding functionality beyond what is already available in order to really make this pattern shine.  We try to keep the Control out of the way most of the time and after binding, just use it to handle requests that require complex UI interaction that can't be dealt with through binding alone.  The view is able to update itself by listening to changes in the model.  The Control also serves to encapsulate as many of the interactions with the user as possible.  This gives us the opportunity to separate out most (if not all) of the required validation logic and user interactions into the Control which we can then pin down with unit tests.   It would be possible to have the view update the model with a declarative syntax which I'll demonstrate in an upcoming article.

Setting up the model:
In order to use this pattern, the model must be constructed so that it broadcasts any state change so they can be bound to the view.  The System.ComponentModel namespace has most of the functionality we will need in order to wire up state changes on our classes, primarily by implementing the INotifyPropertyChanged interface.

image003.png

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}

public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);


The PropertyChangedEventArgs just tells us which property changed:

public class PropertyChangedEventArgs : EventArgs
{
    public PropertyChangedEventArgs(string propertyName);
    public virtual string PropertyName { get; }
}



We'll implement this interface on our domain objects for each property that has a getter and on any methods that change the state of our object's properties.

public class Programmer : INotifyPropertyChanged
{
    #region Constructors
 
    public Programmer(string name)
    {
        m_Name = name;
    }
 
    #endregion
 
    #region Member Variables
 
    private event PropertyChangedEventHandler
        m_PropertyChanged;
 
    private string
        m_Name;
 
    #endregion
 
    #region Properties
 
    public string Name
    {
        get { return m_Name; }
        set
        {
            if (m_Name == value) // guard clause
                return;
 
            m_Name = value;
            FirePropertyChanged("Name");
        }
    }
 
    #endregion
 
    #region Methods
 
    private void FirePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler safeHandler = m_PropertyChanged;
 
        if (null != safeHandler)
            safeHandler(this, new PropertyChangedEventArgs(propertyName));
    }
 
    #endregion
 
    #region INotifyPropertyChanged Members
 
    public event PropertyChangedEventHandler PropertyChanged
    {
        add { m_PropertyChanged += value; }
        remove { m_PropertyChanged -= value; }
    }
 
    #endregion
}


For exposing collections that can be monitored through a standard event, we'll add a reference to Windows.Base in order to get a reference to the ObservableCollection class. 

image004.jpg
 
The ObservableCollection implements the INotifyCollectionChanged event:

public interface INotifyCollectionChanged
{
    event NotifyCollectionChangedEventHandler CollectionChanged;
}

public delegate void NotifyCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs e);


The NotifyCollectionChangedEventArgs holds all the state change information we would need for our view to update itself when it captures the event:

public class NotifyCollectionChangedEventArgs : EventArgs
{
    public NotifyCollectionChangedAction Action { get; }
    public IList NewItems { get; }
    public int NewStartingIndex { get; }
    public IList OldItems { get; }
    public int OldStartingIndex { get; }
}


I usually like to use an abstraction on top of this so we aren't bound to a particular implementation.

public interface IObservableCollection<T> :
    ICollection<T>,
    INotifyCollectionChanged,
    INotifyPropertyChanged
{
}


 Now we can easily create a class that implements our new interface by using the ObservableCollection and we have minimal code required to consume an abstraction of the ObservableCollection's functionality .

public class ModelCollection<T> : ObservableCollection<T>, IObservableCollection<T>
{
    public ModelCollection() : base() { }
    public ModelCollection(List<T> source) : base(source) { }
}



More Binding, Please:

To help our controller shine we'll extend some binding mechanisms by defining a IBindable<T> interface.  This will keep our code structured and consistent and (hopefully) provide 90% of the hooks we need for the controller to control the views.  We want to rely primarily on this binding interface for the controller to push data down to a coarse façade of the view where it will be distributed to widgets (controls) implementing the same interface.

public interface IBindable<T>
{
    void Bind(T value);
}


We'll be building reusable view components that implement IBindable<T> so our coarse form façade can pass the bindings to our widget.  For instance here is a BindableMenuItem<T> that we could reuse across multiple projects.  When we bind to a generic object we'll check if it broadcasts changes to its state by seeing if it implements NotifyPropertyChanged.  If so, we'll wire up the events so we know when to change how our bound object is displayed.

public class BindableMenuItem<T>:MenuItem, IBindable<T>
{
    public BindableMenuItem(T boundInstance)
    {
        Bind(boundInstance);
    }
 
    private T
        m_BoundInstance;
 
    public T BoundInstance
    {
        get { return m_BoundInstance; }
        internal set { Bind(value); }
    }
 
    #region IBindable<T> Members
 
    public void Bind(T value)
    {
        if (null != m_BoundInstance)// already bound to another object... need to unwire first
            Unwire(m_BoundInstance);
 
        if (null != value)
            Wire(value);
    }
 
    protected virtual void Unwire(T value)
    {
        INotifyPropertyChanged notifier = value as INotifyPropertyChanged;
 
        if (null == notifier)
            return;
 
        notifier.PropertyChanged -= BoundInstance_PropertyChanged;
    }
 
    protected virtual void Wire(T value)
    {
        m_BoundInstance = value;
 
        SetText();
 
        INotifyPropertyChanged notifier = value as INotifyPropertyChanged;
 
        if (null == notifier)
            return;
 
        notifier.PropertyChanged += BoundInstance_PropertyChanged;
    }
 
    private void SetText()
    {
        this.Text =  m_BoundInstance.ToString;
    }

 
    protected virtual void BoundInstance_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == m_BoundPropertyName)
            SetText();
    }
 
    #endregion
}


A MenuItem is not normally an item we could bind,  but when we create a derived class that implements IBindable<T> in order to provide a binding mechanism.  I have found using the pattern of having virtual Wire() and Unwire() methods that are executed from inside the interface's Bind() methods allows us to extend the functionality even further if we need to later on.  If you download the code for this article, you'll see that the BindableTreeNode<T> class works in this way when it is extended by the ProgrammerTreeNode class in order to populate the child nodes.
The Supervising Controller pattern works very well when the view we bind to is very coarse (like at the form level).  These high-level binding surfaces act as a Façade and distribute the binding to all of the children components (the widgets/controls in the form).  This way we only really need one Supervising Control per coarse form which reduces object bloat in our project.  This also allows for only the view to be aware of its structure which is now hidden from the control and model so it is much more flexible.  We also keep the control dealing with a relatively light interface with a limited surface are that primarily needs to know how to push the model to the view through our binding mechanism (IBindable<>).

public interface IConventionView:
    IBindable<Programmer>,
    IBindable<IObservableCollection<Programmer>>
{
}


Sadly, there are sometimes a few more functional stubs we have to put on the view to control view behavior that cannot be accessed through binding alone.  In order to keep the code maintainable, the goal should be to keep coupling and our view surface are to a minimum, ideally just through the IBindable<> interface where we pass objects that the view can get change notifications from.  
When we do have to expand our surface are on our view for the controller to hook into the view, we need to be sure the abstractions are on the level of the user interactions and not form-specific in order to keep everything at a consistent level of abstraction and not code directly against form widgets (like buttons).  For instance, if we need to disable a button, it is better to have a method on the view called "DisablePersonRemoval()" rather than "DisableXYZButton()" so we are not syntactically tied to any particular implementation.
The different kinds of reusable bindable widgets are pretty much endless but some are more reusable than others.  I have put together a few for this article to get you started and they are included with the code.
Interacting with the User:
A large portions of the Interactions with the user can be moved to the supervising controller and modeled with a dialog Pub-Sub (publish-subscribe) object that I'll go over in a bit that will serve as a message broker.  The dialog broker is not part of the Supervising Controller pattern, but I have found having a dialog broker to be extremely useful.  This allows us to further isolate the view from any user interaction logic having to do with interacting with the user such as confirmations and validation.  The Supervising Controller can now interact with the view by initiating requests instead of just responding to user input.   This way, more of our code is testable and it makes the view even lighter.

image005.png
 
The interface for the dialog broker is below.  We'll code against the interface to this object so we can change implementation later and can stub out unit tests using the inversion of control pattern (dependency injection) and mock objects.  This is one of the cases where I would choose to expose an interface in addition to providing the class encapsulating the functionality.  This is because the dialog broker is non-domain specific and the surface area and functionality will be relatively stable.  I don't foresee any changes to this object.  I also want to be able to mock this object for testing and basically be able to mock every single method so will code against the interface rather than against an abstract class.

public interface IDialogBroker
{
    void RegisterNotifier<TMessage>(Action<TMessage> onNotify);
    void RegisterResponder<TRequest, TResponse>(Func<TRequest, TResponse> onGetResponse);
 
    void SendNotification<TMessage>(TMessage message);
    TResponse RequestResponse<TRequest, TResponse>(TRequest message);
 
    Boolean HasRegisteredResponder<TRequest, TResponse>();
    Boolean HasRegisteredNotifier<TMessage>();
}


Using the dialog broker allows us to build some reusable dialog widgets for registration with the broker's requests and/or messages.   To me this is a huge bonus because the pattern promotes building reusable view components for registering with the broker that can be used in other projects and reduce coding time.   
When the Form instantiates (our coarse view) when we would instantiate the new controller and register all the interactions the broker needs to handle.  The downside of using the dialog broker is that it must be aware of all the objects that need to be registered in order for the view to function.  I think this is a worthwhile tradeoff if we can keep the number of dialogs to a minimum by ensuring they are non-domain specific.  Anything more complex should be handled by another view with its own supervising controller.  If we don't allow the number or complexity of dialogs to get out of hand, we should be fine.

public partial class Form1 : Form, IConventionView
{
    public Form1()
    {
        InitializeComponent();
       
        m_DialogBroker = new DialogBroker();
        m_Controller = new ConventionSupervisingController(m_DialogBroker, this);
 
        #region Broker Registration
 
        m_DialogBroker.RegisterResponder<String, Boolean>(msg => MessageBox
Show(msg, msg, MessageBoxButtons.YesNo) == DialogResult.Yes);


        m_DialogBroker.RegisterResponder<SelectorRequest<Programmer>, Programmer[]>(request =>
        {
            SelectorDialog<Programmer> selector = new SelectorDialog<Programmer>();
            selector.Bind(request);
            selector.ShowDialog();
            return selector.GetSelection().ToArray();
        });

        m_DialogBroker.RegisterNotifier<String>(msg => MessageBox.Show(msg));

        m_DialogBroker.RegisterResponder<String, StringVerification>(msg =>
        {
            StringVerifierDialog selector = new StringVerifierDialog();
            selector.Bind(msg);
            selector.ShowDialog();
            return selector.GetResponse();
        });

        #endregion
    }
 
    private readonly DialogBroker
        m_DialogBroker;
 
    private readonly ConventionSupervisingController
        m_Controller;
}


Supervising Changes:
The view keeps a reference to the supervising controller and will use it to make change requests.  The controller will handle any dialog necessary with the user to make the change and update the model.

public class ConventionSupervisingController
{
    IDialogBroker DialogBroker { get; set; }
    void AddProgrammer();
    void ChangeSelectedProgrammerName(String newName);
    void AddFriendToSelectedProgrammer(Programmer newFriend);
    void RemoveFriendFromSelectedProgrammer(Programmer removedFriend);
    void AddFriendsToSelectedProgrammer();
    void RemoveFriendsFromSelectedProgrammer();
    void SelectProgrammer(Programmer dude);
}


Again this pattern works very nicely when the form acts as a façade and handles all the widget events that bubble up and sends change requests to the Control.  This makes our user interactions very testable.  We can set the DialogBroker so it acts as a Strategy pattern so in addition to changing how individual dialogs are handled we could change the whole broker on the fly.
Our supervising controller interacts with the dialog broker (and thus the view) by firing off notifications or requesting responses.  For example, here's the implementation for AddProgrammer() method.  You can see how we can model user interactions using the dialog broker in this way so our code is testable.  After the controller is satisfied that all requirements are met through the dialog with the user the broker updates the model.  The model then broadcasts its state change and the view will be updated.

public void AddProgrammer()
{
    StringVerification         response = m_DialogBroker.RequestResponse<String StringVerification>("What is the new programmers name?");
 
    if (!response.Verified)
    {
        m_DialogBroker.SendNotification("Action canceled");
        return;
    }
 
    if (String.IsNullOrEmpty(response.ResponseMessage))
    {
        m_DialogBroker.SendNotification("Invalid name.  Programmer will not be added");
        return;
    }
 
    Programmer newProgrammer = new Programmer(response.ResponseMessage);
 
    m_Convention.Add(newProgrammer);
 
    SelectProgrammer(newProgrammer);
}


In summary: The control encapsulates a large part of the user interactions and validation.  The view just handles changes to bound objects.  Our model is not polluted by any display logic because the control handles everything that is in the "grey" are between the UI and our model.
The Concrete Dialog Broker:
Our concrete dialog broker is a type-safe wrapper around non-type-safe code.  At its core are a couple of (non-type-safe) dictionaries that serve as lookups for the specific type of message being sent to the view or the type of request/response.  When we retrieve the registered notification or request/response object, we know the type of object that is returned (either an  Action<TMessage> in the case of a notifier, or a Func<TRequest, TResponse> in the case of a request/response) because we control the types that get put into our lookups.  

public class DialogBroker : IDialogBroker
{
    public DialogBroker()
    {
        m_NotifierLookup = new Dictionary<Type, object>();
        m_RequestResponseLookup = new Dictionary<Type, Dictionary<Type, object>>();
    }
 
    private readonly Dictionary<Type, Object>
        m_NotifierLookup;
 
    private readonly Dictionary<Type, Dictionary<Type, Object>>
        m_RequestResponseLookup;
 
    #region IMessageBroker Members
 
    public void RegisterNotifier<TMessage>(Action<TMessage> onNotify)
    {
        Type t = typeof(TMessage);
 
        if (m_NotifierLookup.ContainsKey(t))
            m_NotifierLookup[t] = onNotify;
        else
            m_NotifierLookup.Add(t, onNotify);
    }
 
    public void RegisterResponder<TRequest, TResponse>(Func<TRequest, TResponse> onGetResponse)
    {
        Type
            requestType = typeof(TRequest),
            responseType = typeof(TResponse);
 
        // nothing has been registered for a request type
        if (!m_RequestResponseLookup.ContainsKey(requestType))
        {
            m_RequestResponseLookup.Add(requestType, new Dictionary<Type, object>());
            m_RequestResponseLookup[requestType].Add(responseType, onGetResponse);
            return;
        }
 
        // request type has been registered, but not the response type
        if (!m_RequestResponseLookup[requestType].ContainsKey(responseType))
        {
            m_RequestResponseLookup[requestType].Add(responseType, onGetResponse);
            return;
        }
 
        // both request and response types have been registered... this is a replacement
        m_RequestResponseLookup[requestType][responseType] = onGetResponse;
 
    }
 
    public void SendNotification<TMessage>(TMessage message)
    {
        Type t = typeof(TMessage);
 
        (m_NotifierLookup[t] as Action<TMessage>)(message); // call the method
    }
 
    public TResponse RequestResponse<TRequest, TResponse>(TRequest message)
    {
        #region Validation
 
        return (m_RequestResponseLookup[typeof(TRequest)][typeof(TResponse)] as Func<TRequest, TResponse>)(message);
    }
 
    public bool HasRegisteredResponder<TRequest, TResponse>()
    {
        Type
            requestType = typeof(TRequest),
            responseType = typeof(TResponse);
 
        if (!m_RequestResponseLookup.ContainsKey(requestType))
            return false;
 
        if (!m_RequestResponseLookup[requestType].ContainsKey(responseType))
            return false;
 
        return m_RequestResponseLookup[requestType][responseType] != null;
    }
 
    public bool HasRegisteredNotifier<TMessage>()
    {
        Type t = typeof(TMessage);
 
        return
            m_NotifierLookup.ContainsKey(t) &&
            m_NotifierLookup[t] != null;
    }
 
    #endregion
}


The Supervising Controller as a State or Strategy Pattern.

image006.png 


We might need the functionality to change the controller on the fly (strategy pattern) or set the behavior of our app when the application starts (state pattern). This would be good for scenarios where we have different users with different levels of access to our model.  Some users may have access to the complete functionality of the app while some may be very limited in how they can interact with the model.  If our control has all virtual methods, we can just override some of our controller's methods and have a new set of functionality.


public class LimitedConventionSuervisingController : ConventionSupervisingController
{
    public LimitedConventionSuervisingController(IDialogBroker broker, IConventionView view)
        : base(broker, view)
    {
        view.AddMemberEnabled = false;
    }
 
    public override void AddProgrammer()
    {
        DialogBroker.SendNotification("Hey Dude... you are not allowed to do this");
    }
}


Then our controller could be chosen with a factory that takes advantage of our dialog broker to get login information.


public static class SupervisingControllerFactory
{
    public static ConventionSupervisingController Build(IDialogBroker broker, IConventionView view)
    {
        String name = broker.RequestResponse<String, String>("What is your name, my friend?");
 
        if(name.Equals("Dude", StringComparison.OrdinalIgnoreCase))
            return new ConventionSupervisingController(broker, view);
 
        broker.SendNotification("Only 'Dude' can add people... since you are a visitor here you can't");
 
        return new LimitedConventionSuervisingController(broker, view);
    }
}


 And we'll fire off the factory in the constructor of the form to retrieve the appropriate level of access for the user.


public Form1()
{
    InitializeComponent();
   
    m_DialogBroker = new DialogBroker();
 
    #region Broker Registration
    #endregion
 
    m_Controller = SupervisingControllerFactory.Build(m_DialogBroker, this);
 
}


Wrap-up:
Take a look at the attached code for a full example of how to wire up a supervising controller, extend some more simple widgets for binding and see the implementation for our concrete DialogBroker.  The sample has a UI for interacting with a simple model that keeps track of people at a convention and who their friends are.
To select a person, we just click on a node in the tree.

If we change the name of any person, all the view elements displaying that name will automatically update through binding.

I we change the list of friends for any person, the tree will automatically update through binding.

Hopefully this simple UI demonstrates how nice it is to keep the model, view and UI logic separated so we have flexibility to easily change the behavior of our application.
I hope you found this article and the attached code useful.
Until next time,
Happy Coding


Similar Articles