Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Thursday, 16 July 2009
( MVP | Patterns | Testing )
Easy to understand, working example WinForms app using MVP (Model View Presenter) pattern.

Why use this pattern?  Testability and maintainability (ie loosely coupled so less likely to break!)

Download noddy.zip (106.62 KB) (VS2008)

Notes:
View is dumb and does render logic
View never talks to the model (or any business entity eg customer) directly
Presenter retries data and manipulates
Model is the data and business rules

usually MVP sits on top of an ORM or Database Abstraction / Business Logic layer:


How it works:

1) Application comes to Program.cs which calls Form1.cs.. no MVP code here.
static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// Standard code.. nothing MVP
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

2) Form1 (which is the view in MVP) which implements IView interface (**why**).. instantiates a new MainPresenter calling it presenter.
public partial class Form1 : Form, IView
    {
        private MainPresenter presenter;

        public Form1()
        {
            InitializeComponent();
             presenter = new MainPresenter(this);
        }

        private void btnPostCustomer_Click(object sender, EventArgs e)
        {
            presenter.PostCustomer();
        }

        // IView members
        public string FirstName
        {
            get { return txtFirstName.Text; }
            set { txtFirstName.Text = value; }
        }

        public string LastName
        {
            get { return txtLastName.Text; }
            set { txtLastName.Text = value; }
        }

        public void DisplayResult(string result)
        {
            MessageBox.Show(result);
        }
    }
3) MainPresenter constructor runs, and is passed in the view object of type IView. (**could we have just passed it in as a Form object?**)

public class MainPresenter
    {
        private IView view;
        private MainModel model = new MainModel();

        public MainPresenter(IView view)
        {
            this.view = view;    
        }

        public void PostCustomer()
        {
            Debug.Assert(view != null);

            try
            {
                Customer customer = new Customer(view.FirstName, view.LastName);
                model.PostCustomer(customer);
                view.DisplayResult(customer.ToString() + " posted");
            }
            catch(Exception ex)
            {
                view.DisplayResult(ex.Message);
            }
        }
    }
4) App is run, names are filled in, and Post button is submitted.



5) View code (form1.cs) runs presenter.PostCustomer
private void btnPostCustomer_Click(object sender, EventArgs e)
        {
            presenter.PostCustomer();
        }
6) presenter creates a new customer (which could be seen as being outside of MVP, and more in an entity later, as the model layer is usually light, and only relating the modelviews) and passes in the names.  Nothing special on the constructor of customer. 

public void PostCustomer()
        {
            Debug.Assert(view != null);

            try
            {
                Customer customer = new Customer(view.FirstName, view.LastName);
                model.PostCustomer(customer);
                view.DisplayResult(customer.FullName() + " posted");
            }
            catch(Exception ex)
            {
                view.DisplayResult(ex.Message);
            }
        }
7) Presenter calls the main model to PostCustomer which writes out to disk the Customer which has just been entered.
public void PostCustomer(Customer customer)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Customer));

            if (File.Exists("Customer.xml"))
            {
                using (FileStream stream = new FileStream("Customer.xml", FileMode.Append, FileAccess.Write))
                {
                    serializer.Serialize(stream, customer);
                }
            }
            else
            {
                using (FileStream stream = new FileStream("Customer.xml", FileMode.Create, FileAccess.Write))
                {
                    serializer.Serialize(stream, customer);
                }
            }
        }



8) Presenter calls the view to DisplayResult, passing in the customer data as a string.
public void DisplayResult(string result)
        {
            MessageBox.Show(result);
        }


Included in the download are simple tests on the ViewModel and Domain model.  Also Form2 which has a Form2Presenter.  Uses same ViewModel (hmm probably should have a seperate one), and the same domain model (customer.cs).  Next step for me is to get tests working on the Presenters.




| | #