Implementing MVC pattern in .NET CF applications (Part 1).

I would like to start a series of posts devoted to best practices when designing mobile applications. So in this first post I will describe the MVC pattern that I have been using when creating CF applications for the customers.

I realize that a lot has been written on the subject, explaning what the MVC pattern is. If you don't know what it is, you can start from the Wikipedia. The main purpose of the MVC pattern is to decouple data access and business logic from data presentation and user interaction. While Mobile Client Software Factory developed by the P&P group implements it (actually it's a combination of MVP and MVC patterns), I think that their implementation is too heavy and slow for mobile devices. That's why I came up with my own implementation which I think is much more lightweight and simpler than the P&P one. So let's start, shell we?

For simplicity, I am going to create a very straightforward application that contains just a Login form.

1. Interfaces 

First we need a few simple interfaces: IView and IController:

public interface IView

{

     void Show();

     void Hide();

     void Close();

     string Text { get;set;}

}

public interface IController

{

    IView View

    {

        get;

        set;

    }

}

As you can see, IView interface essentially declares methods that exist in any Form, it means that we wouldn't even need to implement them.

2. Add a new form: LoginForm

Let's add a new form, drop a few textboxes and labels (txtUser and txtPassword), add menu items (Exit and OK).  It should look like this:

LoginForm

Since it's a login form, the requirements should be obvious :  when the OK menu is selected take the values from the text boxes and either compare them to some existing values from the local database or via a web service etc... and return the result to the same form and show it in the status label. If we would not use the MVC pattern, we'd just end up with the code that makes appropriate calls to let's say a web service directly in the OK menu event handler, making future modifications for the business logic or UI more difficult. 

3. Add ILoginView interface

public interface ILoginView : IView

{

    void UpdateStatus(string status);

     event EventHandler Exit;

     event EventHandler Login;

     string User { get;}

     string Password { get; }

}

This new interface inherits from our base IView interface.

4. Implement LoginController class

This class is going to hold the logic that will be using the ILoginView interface to communicate with the LoginForm. We add the LoginController class and make sure that it implements the IController interface we've declared earlier:

public class LoginController: IController

{

        private ILoginView view;

        public LoginController(ILoginView view)

        {

            this.view = view;

            this.AttachView(this.view);

        }

        private void AttachView(ILoginView view)

        {

            // Hook up into the view's events

            view.Login += new EventHandler(view_Login);

            view.Exit += new EventHandler(view_Exit);

        }

        void view_Exit(object sender, EventArgs e)

        {

            Application.Exit();

        }

        void view_Login(object sender, EventArgs e)

        {

            // The login event is been raised from the form.

            // Ideally we should pass the user and password to a Business layer to validate against database or a web service

    // For now we'll just compare to some hardcoded values

            if (view.User == "Alex" && view.Password == "test")

            {

                // Update the status on the LoginForm

                view.UpdateStatus("Login successfull.");

            }

            else

            {

                // Update the status on the LoginForm

                view.UpdateStatus("Login failed.");

            }

        }

        #region IController Members

        public IView View

        {

            get

            {

                return view;

            }

            set

            {

                view = (ILoginView)value;

                AttachView(view);

            }

        }

        #endregion

}

5. Implement the ILoginView interface in the LoginForm:

public partial class LoginForm : Form, ILoginView

{

        private string user;

        private string password;

        public LoginForm()

        {

            InitializeComponent();

        }

        #region ILoginView Members

        public void UpdateStatus(string status)

        {

            lblStatusValue.Text = status;

        }

        public event EventHandler Exit;

        public event EventHandler Login;

        public string User

        {

            get

            {

                return user;

            }

        }

        public string Password

        {

            get

            {

                return password;

            }

        }

        #endregion

private void menuOK_Click(object sender, EventArgs e)

{

this.user = txtUser.Text;

this.password = txtPassword.Text;

// Notify the controller

if (this.Login != null)

{

this.Login(this, null);

}

}

private void menuExit_Click(object sender, EventArgs e)

{

// Notify the controller

if (this.Exit != null)

{

this.Exit(this, null);

}

}

}

As you should notice from this code, we don't directly talk to the controller. All what the LoginForm knows about is the ILoginView interface, which it faithfully implements and it just raises the appropriate events for whoever is listening to them. That's all for now. You can be wondering how would you connect the LoginController and ILoginView together without destroying the decoupling that we are trying to achieve. And where do we create an instance of the controller for the view? I will explain it to you in my next post. Meanwhile I welcome your comments or suggestions on how you think we should proceed.