UI Design Using Model-View-Presenter (Part 2)

What Just Happened?

In the previous part, we demonstrated the traditional way of doing things when it comes to UI design. We also reviewed some key concepts like Code Coverage and Separation Of Concerns. Lastly we reviewed some concepts behind the Model-View-Presenter pattern. Yeap, that was a lot of stuff. So, if you haven’t read it yet, be a good boy and go read it now.

Everything has been really theoric up until now, which is boring to depressing levels. But don’t worry. I’ll make it up for you on this part. We are now going to look at how each piece of the pattern is implemented. Alright, let’s move on then, but have in mind this diagram at all times:

MVP Diagram

UI Design Redemption

Ok, time to make things right. First thing to do to sort this mess up, dear reader, is to create the Model.

The Model is a domain object (fancy name for a class full of properties only) which contains the information the View is gonna show. In this case our View will contain a list of clients, so the Model is actually the Client class renamed to obey naming conventions as ClientModel. C’mon– lots of people, including seven devs, two architects and a baby goat were required to figure out that name refactor.

namespace Codenough.Demos.WinFormsMVP
{
   public class ClientModel
   {
      public int Id { get; set; }

      public string Name { get; set; }

      public int Age { get; set; }

      public string Gender { get; set; }

      public string Email { get; set; }
   }
}

As mentioned before (I tend to repeat myself, bear with me here), the Model is a really simple class composed only of properties; better known as plain old C# object or “POCO” for the friends. Wait– “poco” actually means “not much” in Spanish; so, it does not much. Wow, cool– right?

Okay, here is a list of things the Model should NOT do:

  • Do any data validation.
  • Refresh, save or update its data on the database.
  • Throw exceptions like it was something actually funny.
  • Be aware of any other class outside the domain model.
  • Provide a solution to world hunger (actually that would be pretty impressive, but DON’T!).

Next step is to create an interface that will serve as a contract of whatever the View is capable to do so the Presenter is aware of it. Remember it will be communicating with it to serve view data and receive input, so the guy needs to know what is in the view regardless of implementation.

using System;
using System.Collections.Generic;

namespace Codenough.Demos.WinFormsMVP
{
   public interface IClientsView
   {
      event Action ClientSelected;
      event Action Closed;

      IList<ClientModel>; Clients { get; }

      ClientModel SelectedClient { get; }

      void LoadClients(IList<ClientModel> clients);

      void LoadClient(ClientModel client);
   }
}

The interface only defines the methods needed by the Presenter so it can “puppet” it as needed, in order to get its data and validate anything before it gets back into the database; or any other event triggered by the user. Also, see those fancy events on the view interface? Dude, I didn’t even knew interfaces could have events on it, LOL– just kidding. Actually, they are there so the Presenter subscribe to them and then the View can fire them to notify of data changes. Is a two-way communication.

Okay now, to complete the View portion, we need to create the concrete implementation of the view interface. In this case is our old Windows Forms class which has gone under a strict diet low on carbs to reduce its functionality to the minimum.

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Codenough.Demos.WinFormsMVP
{
   public partial class ClientsForm : Form, IClientsView
   {
      public event Action ClientSelected;
      public event Action Closed;

      public ClientsForm()
      {
         this.InitializeComponent();
         this.BindComponent();
      }

      public IList<ClientModel> Clients
      {
         get { return this.clientsListBox.DataSource as IList<ClientModel>; }
      }

      public ClientModel SelectedClient
      {
         get { return this.clientsListBox.SelectedItem as ClientModel; }
      }

      public void LoadClients(IList<ClientModel> clients)
      {
         this.clientsListBox.DataSource = clients;
      }

      public void LoadClient(ClientModel client)
      {
         this.clientNameTextBox.Text = client.Name;
         this.clientEmailTextBox.Text = client.Email;
         this.clientGenderTextBox.Text = client.Gender;
         this.clientAgeTextBox.Text = client.Age.ToString();
      }

      private void BindComponent()
      {
         this.closeButton.Click += OnCloseButtonClick;

         this.clientsListBox.DisplayMember = "Name";
         this.clientsListBox.SelectedIndexChanged += OnClientsListBoxSelectedIndexChanged;
      }

      private void OnClientsListBoxSelectedIndexChanged(object sender, EventArgs e)
      {
         if (this.ClientSelected != null)
         {
            this.ClientSelected();
         }
      }

      private void OnCloseButtonClick(object sender, EventArgs e)
      {
         if (this.Closed != null)
         {
            this.Closed();
         }
      }
   }
}

Now the View implementation only loads data from someone else and fires events as the loaded data changes so anyone out there in space listening to it be aware of the changes and will take appropriate action (OK, that sounded like a line from a Law & Order episode). As you might have already guessed, the only component aware of the View events would be the Presenter. So, the next step is to implement the infamous Presenter class.

The Presenter is a component that is aware of what is happening in the View through its events (See what I said about repeating myself? But hopefully that will help get things carved out in your head, bud.). As we mentioned before, the Presenter talks to the View by using its interface instead of the concrete implementation, the ClientsForm class that is; this for the sake of code testability. Later on, we are going to provide the Presenter with a “mock” instead of the actual View implementation. We are going to learn what a mock is later, no need to hurry.

using System.Linq;

namespace Codenough.Demos.WinFormsMVP
{
   public class ClientsPresenter
   {
      private readonly IClientsView view;
      private readonly ClientRepository clientsRepository;

      public ClientsPresenter(IClientsView view, ClientRepository clientsRepository)
      {
         this.view = view;
         this.clientsRepository = clientsRepository;

         var clients = clientsRepository.FindAll();

         this.view.ClientSelected += OnClientSelected;
         this.view.LoadClients(clients);

         if (clients != null)
         {
            this.view.LoadClient(clients.First());
         }
      }

      public void OnClientSelected()
      {
         if (this.view.SelectedClient != null)
         {
            var id = this.view.SelectedClient.Id;
            var client = this.clientsRepository.GetById(id);

            this.view.LoadClient(client);
         }
      }
   }
}

This time around, the one handling data access through the repository class is the Presenter. The View is no longer aware of where its data is coming from; that’s a good thing. Also, we can notice how the Presenter reacts each time a client is selected on the list box, since it has subscribed to the View’s ClientSelected event. Fancy, right?

Now, the only thing left to do is to wire-up everything so we can run this incredibly clever application which will save many companies from bankruptcy from now on to the future. Of course, that is for the next part in our series. Gotcha, huh?

Coming Up

In our next part, we will wire up all this mess so we have a nice and working application. We will also implement all unit tests we defined in the first part. Is going to be lots of fun, I promise.

Go read Part 3! ;)

About these ads

2 thoughts on “UI Design Using Model-View-Presenter (Part 2)

  1. Pingback: UI Design Using Model-View-Presenter (Part 3) | Informatech CR Blog

  2. Pingback: UI Design Using Model-View-Presenter (Part 1) | Informatech CR Blog

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s