Blue Theme Orange Theme Green Theme Red Theme
 
Team Foundation Server Hosting
Home | Forums | ASP.NET 2.0 Tutorials | Web Services | How Do I...? | Class Browser | WPF Quick Starts | Advertise with Us
 | Consulting  
Submit an Article Submit a Blog 
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
Team Foundation Server Hosting
Search :       Advanced Search »
Home » Design & Architecture » Introduction to Model View Control (MVC) Pattern using C#

Introduction to Model View Control (MVC) Pattern using C#

The benefits of using the Model-View-Control (MVC) pattern in our development projects is that we can completely decouple our business and presentation application layers. Furthermore, we will have a completely independent object to control the presentation layer.

Author Rank :
Page Views : 47998
Downloads : 919
Rating :
 Rate it
Level : Intermediate
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
ModelViewControllerArticle.zip
 
 
Nevron Gauge for SharePoint
Become a Sponsor
 Tag Cloud
 Latest Jobs
More ... 
 Latest Interview Questions
More ... 

Benefits

The benefits of using the Model-View-Control (MVC) pattern in our development projects is that we can completely decouple our business and presentation application layers. Furthermore, we will have a completely independent object to control the presentation layer. The independence between the objects/layers in our project that the MVC provides will make maintenance somewhat easier and code reuse very easy (as you'll see below).

As a general practice we know we want to keep the object dependencies in our projects to a minimum so changes are easy and we can reuse the code we've worked so hard on. To accomplish this we will follow a general principle of "programming to the interface, not the class" using the MVC pattern.

Our mission, if we choose to accept it...

We have been commissioned to build a ACME 2000 Sports Car business object and our task is to create a simple windows interface to (1) display the vehicle's current direction and speed, and (2) enable the end user to change direction, accelerate, and decelerate.  And of course, there will be scope creep. 

There are already rumors at ACME that if our project is successful, we will eventually a need to develop a similar interface for the ACME 2 Pickup Truck and the ACME 1 Tricycle. As developers, we also know that the ACME management team will eventually say "Hey, this is really cool. Can we see it on the company's intranet?" All of this in mind, we want to deliver a product that will be easily scalable so we can be sure to have food on our plates for some time.

So, coincidentally, we think... "This is a perfect situation to use the MVC!!".

Our Architecture Overview

Ok, now we know that we want to use the MVC, we need to figure out what the heck it is. Through our research we come up with the three parts of the MVC: The Model, Control, and View. In our system, the Model would be our car, the View would be the user interface, and the Control is what ties the two together.


 
To make any changes to the Model (our ACME 2000 sports car), we'll be using our Control. Our Control will make the request to the Model (our ACME 2000 sports car), and update our View, which is our user interface (UI).

This seems simple enough, but here's the first problem we have to solve: What happens when the end user wants to make a change to our ACME 2000 sports car, such as going faster or turning? They are going to have to do it by requesting a change using the Control, through the View (our windows form).


 
Now we are left with one last problem to solve. What if the View doesn't have the necessary information to display the current state of the Model?  We're going to have to add one more arrow to our diagram: The View will be able to request the Model's state in order to get what it needs to display the information about the state of the Model.

 

Finally, our end user (our driver) will be interacting with our entire ACME Vehicle Control system through the View.  If they want to request a change to the system, such as adding a bit of acceleration, the request will be initiated from the View and handled by the Control

The Control will then ask the Model to change and make any necessary changes to the View. For example if the ACME 2000 Sports Car has a "floor it" request from an unruly driver and is now traveling to fast to make a turn, the Control will know to disable the ability to turn in the View, thus preventing a catastrophic pileup in the middle of rush-hour (whew!).

The Model (the ACME 2000 Sports Car) will notify the View that it's speed has increased and the View will update where appropriate.

So after all that, here's the overview of what we will be building:


 
Getting Started: Parts... Parts... Parts...

Being developers who always think ahead, we want to be sure our system will have a long and prosperous life. This means being prepared for as many changes at ACME as possible. In order to do this we know to follow two golden rules... "keep your classes loosely coupled" and, in order to accomplish this... "program to the interface".

So we will make three interfaces (as you may have guessed, one for the Model, one for the View, and one for the Control).

After much research and laborious interviews with the folks at ACME, we find out more about the system specifications. We want to be sure that we can set the maximum speeds for traveling forward, backwards ,and turning. We also need to be able to speed up, slow down, and turn left and right. Our "dashboard" view must display the current speed and direction.

It's a tall order to implement all of these requirements, but we're sure we can handle it...

First, let's take care of some preliminary items.  We'll need something to represent the direction and turn requests.  We'll create two enumerables, AbsoluteDirection and RelativeDirection.

public enum AbsoluteDirection
{
North=0, East, South, West
}

public enum RelativeDirection
{
Right, Left, Back
}

Next, let's tackle the Control interface. We know the Control has to pass requests to the Model, specifically: Accelerate, Decelerate, and Turn. We'll create an IVehicleControl interface with the appropriate methods.

public interface IVehicleControl
{
void Accelerate(int paramAmount);
void Decelerate(int paramAmount);
void Turn(RelativeDirection paramDirection);
}

Now we'll put together the Model interface.  We need to know the Vehicle's name, speed, maximum speed, maximum reverse speed, maximum turn speed and direction. We also need the methods to accelerate, decelerate, and turn.

public interface IVehicleModel
{
string Name{ get; set;}
int Speed{ get; set;}
int MaxSpeed{ get;}
int MaxTurnSpeed{ get;}
int MaxReverseSpeed { get;}
AbsoluteDirection Direction{
get; set;}
void Turn(RelativeDirection paramDirection);
void Accelerate(int paramAmount);
void Decelerate(int paramAmount);
}

And Finally, we'll put together the View interface. We know the view should expose some functionality to the Control, such as enabling and disabling acceleration, deceleration, and turn requests.

public class IVehicleView
{
void DisableAcceleration();
void EnableAcceleration();
void DisableDeceleration();
void EnableDeceleration();
void DisableTurning();
void EnableTurning();
}

Now we have to make a few tweaks to our interfaces to allow them to interact. First of all, any Control should be aware of it's View and Model, so we'll add "SetModel" and "SetView" methods to our IvehicleControl interface:

public interface IVehicleControl
{
void RequestAccelerate(int paramAmount);
void RequestDecelerate(int paramAmount);
void RequestTurn(RelativeDirection paramDirection);
void SetModel(IVehicleModel paramAuto);
void SetView(IVehicleView paramView);
}

The next part is a bit tricky. We want the View to be aware of changes in the Model. To do this we'll use a GOF design pattern "Observer".

To implement the Observer pattern, we need to add the following methods to the Model (which will be "observed" by the View): AddObserver, RemoveObserver, and NotifyObservers.

public interface IVehicleModel
{
string Name{ get; set;}
int Speed{ get; set;}
int MaxSpeed{ get;}
int MaxTurnSpeed{ get;}
int MaxReverseSpeed { get;}
AbsoluteDirection Direction{
get; set;}
void Turn(RelativeDirection paramDirection);
void Accelerate(int paramAmount);
void Decelerate(int paramAmount);
void AddObserver(IVehicleView paramView);
void RemoveObserver(IVehicleView paramView);
void NotifyObservers();
}

...and add the following method to the View (which will be "observing" the Model). What will happen is the Model will have a reference to the View. When the Model changes, it will call the NotifyObservers() method and pass a reference to itself and notify the View of a change by calling the Update() method of the View. (It will become clear as mud when we wire everything up later).

public class IVehicleView
{
void DisableAcceleration();
void EnableAcceleration();
void DisableDeceleration();
void EnableDeceleration();
void DisableTurning();
void EnableTurning();
void Update(IVehicleModel paramModel);
}

So now we have our interfaces put together. We are only going to use references to these interfaces in the rest of our code to ensure we have loose coupling (which we know is a good thing). Any user interface that shows the state of a Vehicle will implement IVehicleView, all of our ACME automobiles will implement IVehicleModel, and we'll make controls for our ACME automobiles with ACME vehicle controls which will implement IVehicleControl.

Next... What things will have things in common?

We know all our vehicles should act the same, so we're going to create a common code base "skeleton" to handle their operation. This is going to be an abstract class because we don't want anyone driving around a skeleton (you can't make an instance of an abstract class). We'll call it Automobile. We'll use an ArrayList (from System.Collections) to keep track of all the interested Views (remember the Observer pattern?). We could have used a plain old array of IVehicleView references, but we're all getting fatigued at this point and want to get through this article. If you're interested, check out the implementation of the AddObserver, RemoveObserver, and NotifyObservers methods to get an idea of how the Observer pattern works by helping our IVehicleModel interact with the IVehicleView. Every time there is a change in speed or direction, the Automobile notifies all IVehicleViews

public abstract class Automobile: IVehicleModel
{
#region "Declarations "
private ArrayList aList = new ArrayList();
private int mintSpeed = 0;
private int mintMaxSpeed = 0;
private int mintMaxTurnSpeed = 0;
private int mintMaxReverseSpeed = 0;
private AbsoluteDirection mDirection = AbsoluteDirection.North;
private string mstrName = "";
#endregion
#region
"Constructor"
public Automobile(int paramMaxSpeed, int paramMaxTurnSpeed, int paramMaxReverseSpeed, string paramName)
{
this.mintMaxSpeed = paramMaxSpeed;
this.mintMaxTurnSpeed = paramMaxTurnSpeed;
this.mintMaxReverseSpeed = paramMaxReverseSpeed;
this.mstrName = paramName;
}
#endregion
#region
"IVehicleModel Members"
public void AddObserver(IVehicleView paramView)
{
aList.Add(paramView);
}
public void RemoveObserver(IVehicleView paramView)
{
aList.Remove(paramView);
}
public void NotifyObservers()
{
foreach(IVehicleView view in aList)
{
view.Update(
this);
}
}
public string Name
{
get
{
return this.mstrName;
}
set
{
this.mstrName = value;
}
}
public int Speed
{
get
{
return this.mintSpeed;
}
}
public int MaxSpeed
{
get
{
return this.mintMaxSpeed;
}
}
public int MaxTurnSpeed
{
get
{
return this.mintMaxTurnSpeed;
}
}
public int MaxReverseSpeed
{
get
{
return this.mintMaxReverseSpeed;
}
}
public AbsoluteDirection Direction
{
get
{
return this.mDirection;
}
}
public void Turn(RelativeDirection paramDirection)
{
AbsoluteDirection newDirection;
switch(paramDirection)
{
case RelativeDirection.Right:
newDirection = (AbsoluteDirection)((
int)(this.mDirection + 1) %4);
break;
case RelativeDirection.Left:
newDirection = (AbsoluteDirection)((
int)(this.mDirection + 3) %4);
break;
case RelativeDirection.Back:
newDirection = (AbsoluteDirection)((
int)(this.mDirection + 2) %4);
break;
default:
newDirection = AbsoluteDirection.North;
break;
}
this.mDirection = newDirection;
this.NotifyObservers();
}
public void Accelerate(int paramAmount)
{
this.mintSpeed += paramAmount;
if(mintSpeed >= this.mintMaxSpeed) mintSpeed = mintMaxSpeed;
this.NotifyObservers();
}
public void Decelerate(int paramAmount)
{
this.mintSpeed -= paramAmount;
if(mintSpeed <= this.mintMaxReverseSpeed) mintSpeed = mintMaxReverseSpeed;
this.NotifyObservers();
}
#endregion
}

Last but not least...

Now that our "ACME Framework" is in place, we just have to set up our concrete classes and our interface. Let's take care of the last two class first which will be our Control and our Model...

Here's our concrete AutomobileControl which implements the IVehicleControl interface. Our AutomobileControl will also set the View depending on the state of the Model (check out the SetView method which is called every time there is a request passed to the Model).

Notice, we just have references to the IVehicleModel (not the Automobile abstract class) to keep things loose and IVehicleView (not a specific View).

public class AutomobileControl: IVehicleControl
{
private IVehicleModel Model;
private IVehicleView View;
public AutomobileControl(IVehicleModel paramModel, IVehicleView paramView)
{
this.Model = paramModel;
this.View = paramView;
}
public AutomobileControl()
{
}
#region IVehicleControl Members
public void SetModel(IVehicleModel paramModel)
{
this.Model = paramModel;
}
public void SetView(IVehicleView paramView)
{
this.View = paramView;
}
public void RequestAccelerate(int paramAmount)
{
if(Model != null)
{
Model.Accelerate(paramAmount);
if(View != null) SetView();
}
}
public void RequestDecelerate(int paramAmount)
{
if(Model != null)
{
Model.Decelerate(paramAmount);
if(View != null) SetView();
}
}
public void RequestTurn(RelativeDirection paramDirection)
{
if(Model != null)
{
Model.Turn(paramDirection);
if(View != null) SetView();
}
}
#endregion
public void SetView()
{
if(Model.Speed >= Model.MaxSpeed)
{
View.DisableAcceleration();
View.EnableDeceleration();
}
else if(Model.Speed <= Model.MaxReverseSpeed)
{
View.DisableDeceleration();
View.EnableAcceleration();
}
else
{
View.EnableAcceleration();
View.EnableDeceleration();
}
if(Model.Speed >= Model.MaxTurnSpeed)
{
View.DisableTurning();
}
else
{
View.EnableTurning();
}
}
}

Here's our ACME200SportsCar class (which extends the Automobile abstract class which implements the IVehicleModel interface):

public class ACME2000SportsCar:Automobile
{
public ACME2000SportsCar(string paramName):base(250, 40, -20, paramName){}
public
ACME2000SportsCar(string paramName, int paramMaxSpeed, int paramMaxTurnSpeed, int paramMaxReverseSpeed):
base(paramMaxSpeed, paramMaxTurnSpeed, paramMaxReverseSpeed, paramName){}
}

And now for our View...

Now we have to create the last of our three ACME MVC components... the View

We'll create a AutoView user control and have it implement the IVehicleView interface. The AutoView component will have references to our control and model interfaces:

public class AutoView : System.Windows.Forms.UserControl, IVehicleView
{
private IVehicleControl Control = new ACME.AutomobileControl();
private IVehicleModel Model = new ACME.ACME2000SportsCar("Speedy");
}

We also need to wire everything up in the constructor for the UserControl.

public AutoView()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
WireUp(Control, Model);
}
public void WireUp(IVehicleControl paramControl, IVehicleModel paramModel)
{
// If we're switching Models, don't keep watching
// the old one!
if(Model != null)
{
Model.RemoveObserver(
this);
}
Model = paramModel;
Control = paramControl;
Control.SetModel(Model);
Control.SetView(
this);
Model.AddObserver(
this);
}

Next, we'll add our buttons, a label to display the status of the ACME2000 Sports Car and a status bar just for kicks and fill out the code for all the buttons. 

private void btnAccelerate_Click(object sender, System.EventArgs e)
{
Control.RequestAccelerate(
int.Parse(this.txtAmount.Text));
}
private void btnDecelerate_Click(object sender, System.EventArgs e)
{
Control.RequestDecelerate(
int.Parse(this.txtAmount.Text));
}
private void btnLeft_Click(object sender, System.EventArgs e)
{
Control.RequestTurn(RelativeDirection.Left);
}
private void btnRight_Click(object sender, System.EventArgs e)
{
Control.RequestTurn(RelativeDirection.Right);
}

Add a method to update the interface...

public void UpdateInterface(IVehicleModel auto)
{
this.label1.Text = auto.Name + " heading " + auto.Direction.ToString() + " at speed: " + auto.Speed.ToString();
this.pBar.Value = (auto.Speed>0)? auto.Speed*100/auto.MaxSpeed : auto.Speed*100/auto.MaxReverseSpeed;
}

Finally, we'll wire up the IVehicleView interface methods...

public void DisableAcceleration()
{
this.btnAccelerate.Enabled = false;
}
public void EnableAcceleration()
{
this.btnAccelerate.Enabled = true;
}
public void DisableDeceleration()
{
this.btnDecelerate.Enabled = false;
}
public void EnableDeceleration()
{
this.btnDecelerate.Enabled = true;
}
public void DisableTurning()
{
this.btnRight.Enabled = this.btnLeft.Enabled = false;
}
public void EnableTurning()
{
this.btnRight.Enabled = this.btnLeft.Enabled = true;
}
public void Update(IVehicleModel paramModel)
{
this.UpdateInterface(paramModel);
}

And we're off!!!

Now we can go for a test drive in the ACME2000 Sports Car. Everything is going as planned and then we run into an ACME executive who wants to drive a pickup truck instead of a sports car. 

Good thing we used the MVC! All we need to do is create a new ACMETruck class, wire it up, and we're in business!

public class ACME2000Truck: Automobile
{
public ACME2000Truck(string paramName):base(80, 25, -12, paramName){}
public ACME2000Truck(string paramName, int paramMaxSpeed, int paramMaxTurnSpeed, int paramMaxReverseSpeed):
base(paramMaxSpeed, paramMaxTurnSpeed, paramMaxReverseSpeed, paramName){}
}

in the AutoView, we just have to build the truck and wire it up!

private void btnBuildNew_Click(object sender, System.EventArgs e)
{
this.autoView1.WireUp(new ACME.AutomobileControl(), new ACME.ACME2000Truck(this.txtName.Text));
}

If we wanted a new Control that only allowed us to increase or decrease the speed by a maximum of 5mph, it's a snap!  Create a SlowPokeControl (same as our AutoControl, but with limits on how much a Model will be requested to accelerate)

public void RequestAccelerate(int paramAmount)
{
if(Model != null)
{
int amount = paramAmount;
if(amount > 5) amount = 5;
Model.Accelerate(amount);
if(View != null) SetView();
}
}
public void RequestDecelerate(int paramAmount)
{
if(Model != null)
{
int amount = paramAmount;
if(amount > 5) amount = 5;
Model.Accelerate(amount);
Model.Decelerate(amount);
if(View != null) SetView();
}
}

If we want to make our ACME2000 Truck a SlowPoke, we just wire it up in the AutoView!

private void btnBuildNew_Click(object sender, System.EventArgs e)
{
this.autoView1.WireUp(new ACME.SlowPokeControl(), new ACME.ACME2000Truck(this.txtName.Text));
}

And finally, if we wanted a web-enabled interface, all we have to do is create a web project and on the UserControl implement the IVehicleView interface!

In Conclusion...

As you can see, using the MVC to help build code to control interfaces that is very loosely coupled makes life much easier when it comes to change requests. It also makes the impact of changes negligible and you can reuse your interfaces and abstract classes almost anywhere. There are a couple of places where we can build some more flexibility in our project, especially in terms of requesting changes to our Model's state, but that will have to wait for next time.

In the meanwhile, keep the MVC in mind for your next project... You won't regret it.

Happy Driving.

Comment Request!
Thank you for reading this post. Please post your feedback, question, or comments about this post Here.
Login to add your contents and source code to this article
 [Top] Rate this article
 
 About the author
 
Matthew Cochran
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
ASP.NET 4 Hosting
Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites – Click Here!
 
 Post a Feedback, Comment, or Question about this article
Subject:
Comment:
Nevron Gauge for SharePoint
Become a Sponsor
 Comments
hi by ashish On August 18, 2009
Very good article indeed. but i was wondering how will it maintain its state when used on web application. 
i mean we need to maintain the state of model so that its values like speed , name , maxspeed etc is maintained across posts. 
if not, ever time we click on acelerate or decelerate , the value enetere in the text box will be set as the new speed of the mdoel instead of increasing or decreasing the previous speed.

thanks in advance

Reply | Email | Modify 
Awesome Article Matthew by Muzikayise On September 24, 2009
Awesome Article!
This is the second article I've read which has been created by you and I must say both have been satisfactory to read and indeed great for learning.

Thanks for sharing your knowledge

Best

Muzi

Reply | Email | Modify 
Added a real life example based on this article by Wolfgang On September 20, 2010
Hi Matthew,

thanks for this great article which helped me to build a real life example. I described my MVC implementation in a blog (link: http://www.dotnetheaven.com/UploadFile/Blogs/3655/) which might help others to use your information even better.

regards
Wolfgang
Reply | Email | Modify 
6 Months Free & No Setup Fees ASP.NET Hosting!
 © 2012  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.