Wednesday, July 16, 2008

Model View Controller - C# implementation

Model view controller is a classic pattern that is used in applications that need ability to maintain multiple views with same data. It can be used as an architectural pattern as well as a design pattern. MVC helps us to decouple the presentation layer and business layer (domain logic layer). The presentation layer is further divided into the View and Controller.

The MVC abstraction can be graphically explained as given below:

The main participants are

Model: The business entity/ domain data on which the application operates. Many applications use a persistent storage mechanism (such as a database) to store data. MVC does not specifically mention the data access layer because it is understood to be underneath or encapsulated by the Model.

View: The user interface that renders the model into a form of interaction.

Controller: Handles a request from a view and updates the model resulting a change in Model’s state.

To implement MVC in .NET we need mainly three classes (View, Controller and the Model). This example is implemented using interfaces that will allow me to make my application loosely coupled.

I have the interfaces (IView, IModel, IController) implemented as

public interface IView

{

//Method to change the state of view according to the model changes

void UpdateView(IModel model);

//These methods are used by the controller to change the state of view

void SalaryIncreased(Boolean increased);

void CompanyChanged(Boolean changed);

void DesignationChanged(Boolean changed);

}

public interface IModel

{

String UserName { get; set; }

String Company { get; set; }

String Designation { get; set; }

Int32 Salary { get; set; }

//Implementing the observer pattern. The model uses an observer

//pattern implementation to notify the views about the changes in the

//model

void AddObserver(IView view);

void RemoveObserver(IView view);

void Notify();

//Observer pattern implementation methods. End

}

public interface IController

{

//The methods to change the state of Model

void ChangeSalary(Int32 salary);

void ChangeCompany(String company);

void ChagneDesignation(String designation);

//Storing the Model and View instances in the controller

IModel Model { get; set; }

IView View { get; set; }

}

In my sample application I have a User class (Model) and a Form that helps users to change the User properties (View). I also have a UserController class that acts as a controller for the View.

The model

public class User: IModel

{

private String userName;

private String company;

private String designation;

private Int32 salary;

//List of observers. You can add the views that should be notified the change on Model in this

//collection.

List<IView> observers = new List<IView>();

public User() { }

public User(String userName, String company, String designation, Int32 salary)

{

this.userName = userName;

this.company = company;

this.designation = designation;

this.salary = salary;

}

#region IModel Members

public string UserName

{

get

{

return this.userName;

}

set

{

this.userName = value;

}

}

public string Company

{

get

{

return this.company;

}

set

{

this.company = value;

this.Notify();

}

}

public string Designation

{

get

{

return this.designation;

}

set

{

this.designation = value;

this.Notify();

}

}

public int Salary

{

get

{

return this.salary;

}

set

{

this.salary = value;

this.Notify();

}

}

//Adding the View into the Observer collection

public void AddObserver(IView view)

{

observers.Add(view);

}

public void RemoveObserver(IView view)

{

if (observers.Contains(view))

observers.Remove(view);

}

//Iterates through the observers and notify them about the model change.

public void Notify()

{

foreach (IView view in observers)

{

view.UpdateView(this);

}

}

#endregion

}

The View

public partial class UserView : Form, IView

{

//The controller instance for the View.

private IController Controller = new UserController();

//Creating a model. (Sample).

private IModel Model = new User("Arun", "EDS", "Tech Lead", 50000);

public UserView()

{

InitializeComponent();

//Wire the controller and model to the View

WireUpControllerModel(Model, Controller);

//Update the View with the model's current state

this.UpdateView(Model);

}

private void _companycomboBox_SelectedIndexChanged(object sender, EventArgs e)

{

Controller.ChangeCompany(_companycomboBox.Text);

}

private void _designationComboBox_SelectedIndexChanged(object sender, EventArgs e)

{

Controller.ChagneDesignation(_designationComboBox.Text);

}

private void _changeSalaryButton_Click(object sender, EventArgs e)

{

Controller.ChangeSalary(Convert.ToInt32(_newSalaryTextBox.Text));

}

#region IView Members

public void UpdateView(IModel model)

{

_userNameTextBox.Text = model.UserName;

_designationTextBox.Text = model.Designation;

_companyTextBox.Text = model.Company;

_salaryTextBox.Text = model.Salary.ToString();

}

private void WireUpControllerModel(IModel model, IController controller)

{

if (Model != null)

Model.RemoveObserver(this);

Model = model;

Controller = controller;

Controller.Model = this.Model;

Controller.View = this;

Model.AddObserver(this);

}

public void SalaryIncreased(bool increased)

{

if (increased)

_salaryChangeLabel.Text = "Salary Increased!!!";

else

_salaryChangeLabel.Text = "Salary Decreased!!!";

}

public void CompanyChanged(bool changed)

{

if (changed)

_companyChangeLabel.Text = "Company Changed!!!";

}

public void DesignationChanged(bool changed)

{

if (changed)

_designationChangeLabel.Text = "Designation Changed!!!";

}

#endregion

}

The controller:

public class UserController : IController

{

private IView view;

private IModel model;

public UserController(IView view, IModel model)

{

this.view = view;

this.model = model;

}

public UserController() { }

#region IController Members

public void ChangeSalary(int salary)

{

if (Model.Salary > salary)

View.SalaryIncreased(false);

else

View.SalaryIncreased(true);

Model.Salary = salary;

}

public void ChangeCompany(string company)

{

if (Model.Company == company)

View.CompanyChanged(false);

else

View.CompanyChanged(true);

Model.Company = company;

}

public void ChagneDesignation(string designation)

{

if (Model.Designation == designation)

View.DesignationChanged(false);

else

View.DesignationChanged(true);

Model.Designation = designation;

}

public IModel Model

{

get

{

return this.model;

}

set

{

this.model = value;

}

}

public IView View

{

get

{

return this.view;

}

set

{

this.view = value;

}

}

#endregion

}

8 comments:

free online lotto said...

Yugs, daw nabasahan ko naman ni sa iban nga blog?

edurazee said...

You damn so called Gurus always make one fucking mistake.

And that is, making things hard to understand.

You are discussing MVC here.

Then where does Obsrver pattern come from?

Anonymous said...

Observer pattern is there to remove the dependency of the Model on the View (ie. to prevent a tightly coupled architecture).

Without the observer pattern:
Imagine the methods on the View changes. You have to update the Model with the new View methods.

With the observer pattern:
The View can change independantly of the Model.

So with the oberserver pattern, the Model "notifies" the View of model changes without being dependant on the view.

MVC and Observer go together fairly often to remove this dependency.

Anonymous said...

you explained it nicely, thanks.

Saelen Kenny said...

Nice post !
I have one question though...

The controller - Model link is setup in the form itself but shouldn't that code be placed in the entry point of your application apart from the view??

Or maybe here it is just done for testing purposes and because it does not have influence on the subject here..

Anonymous said...

The controller should not be instantiated in the view. It needs to be outside the view. This allows you to have multiple views working on the same model data. The controller should be created at entry point. (main method).

Michal said...

Nicely explained, thanks a lot. :)

Simmy said...

If I want to change the View of the model back to its original state, thus reseting TextLabel to what it was before, how do I do this?