Click here to Skip to main content
Click here to Skip to main content

A Practical Use of the MVC Pattern

By , 8 Jan 2007
 

Sample Image - MvcTest.gif

Table of Contents

  1. Introduction
  2. Background
  3. An Example
  4. Description of the MVC
  5. Benefits of the MVC
  6. Drawbacks
  7. How It Works / Dissecting the Sample Application
  8. Conclusions
  9. References
  10. History

1. Introduction

There are many articles here on The Code Project that detail the working of the MVC pattern. So you may ask why we need another one? In this article, I try to present a simple and clear use of the MVC pattern.

The demo project uses ZedGraph for generating the chart (http://www.codeproject.com/csharp/zedgraph.asp) and an
implementation of the BackgroundWorker found in .NET 2.0 ported for .NET 1.1 by Juval Lovy (http://www.idesign.net/).

2. Background

So What is the MVC Pattern?

As the GoF book states:

"MVC decouples views and models by establishing a subscribe/notify protocol between them. A view must ensure that its appearance reflects the state of the model. Whenever the model's data changes, the model notifies views that depend on it. In response, each view gets an opportunity to update itself. This approach lets you attach multiple views to a model to provide different presentations. You can also create new views for a model without rewriting it."

3. An Example

An example of the use of this pattern would be a reporting system where the same data needs to be presented to the user in different ways. Like an Excel sheet may be presented as columns and rows or by using a chart. Another need of this pattern as I encountered recently was the use of the same data in a desktop application and in a GUI developed for touch-screen devices.

The example in this article shows some test data in a listview and a chart representation using ZedGraph.
This design decouples the views from the model that generates the test data.

4. Description of the MVC

Most modern applications are separated into separate layers: presentation (GUI), business logic (how and why we do things), data layer (persistence).

The MVC pattern is separated similarly as a:

  • Model – manipulates the raw data that the application uses (calculations, enforcing business rules, querying the database, etc). The data provided by the model is independent of the visual representation – so it can be used by any number of views without code redundancy!
  • View – renders the model into a user interface (the visual representation of the data). The view is isolated from data operations.
  • Controller – processes and responds to events, defines the way the user interface (the view) and the model reacts to user input. The user triggers the events that change the model which in turn notifies the registered views to update/refresh their data.
    Mvc diagram

5. Benefits of the MVC

Because the model is decoupled from the view, it allows a great flexibility to implement the model using code reusability and modularity.

The inner working of the model can be changed any time without any effect on the view as long as the output remains the same.

Parallel development process for model and view!

6. Drawbacks

  • Adds more complexity to the development
  • Not suitable for small applications

7. How It Works /Dissecting the Sample Application

You could start by either developing the model or the view.
Let’s start by describing the model. I created an abstract base class for all models as follows:

public abstract class BaseModel
{
    /// <SUMMARY>
    /// Event fired when the model is finished calculating/processing the data.
    /// </SUMMARY>
    public event EventHandler ModelChanged;
    /// <SUMMARY>
    /// Event fired when the model is calculating the data to notify the view
    /// about its progress.
    /// </SUMMARY>
    public event ProgressEventHandler ModelProgress;

    #region protected members
    protected BackgroundWorker backgroundWorker;
    protected DataSet ds;
    #endregion protected members

    #region constructor
    protected BaseModel()
    {
        ds = new DataSet();
        backgroundWorker = new BackgroundWorker();

        backgroundWorker.WorkerReportsProgress = false;
        backgroundWorker.WorkerSupportsCancellation = true;
    }
    #endregion constructor
    
    #region public abstract methods
    public abstract void GenerateReport();
    #endregion public abstract methods

    #region public methods
    /// <SUMMARY>
    /// Returns the report data.
    /// </SUMMARY>
    public DataSet QueryModel()
    {
        return ds;
    }

    public void CancelBackgroundWorker()
    {
        backgroundWorker.CancelAsync();
    }
    #endregion public methods

    #region event firing methods
    protected void Fire_ModelChanged(object sender, RunWorkerCompletedEventArgs ea)
    {
        BackgroundWorker bw = sender as BackgroundWorker;
        if (bw != null)
            bw.RunWorkerCompleted -= 
		new RunWorkerCompletedEventHandler(Fire_ModelChanged);

        if (ModelChanged != null)
            ModelChanged(this, EventArgs.Empty);
    }

    protected void Fire_ModelProgress(object sender, int percent)
    {
        if (ModelProgress != null)
            ModelProgress(this, percent);
    }
    #endregion event firing methods
}

The base class for models contains a BackgroundWorker and a dataset object. I use a modified BackgroundWorker implementation of Juval Lovy. This way we can do the calculations in a separate thread and have a responsive user interface. The dataset simply stores the test data that the view uses.

The Model_Income class derives from the BaseModel and adds a table with two columns to the dataset that is filled with test data in the overridden GenerateReport method.

There are two events that the BaseModel exposes:

  • ModelChanged – fired when the model has finished generating the data needed by the view.
  • ModelProgress – fired every time the model wants to notify the view of the progress made in calculating the data.
    The controller subscribes the views to these events exposed by the model.

Role of the Model: Generate the Data, Fire Events when Needed to Notify the View of its State.

The view is also derived from a base class BaseView that in turn exposes one or more events and public methods for calling from the controller.

public class BaseView : Form
{
    public event EventHandler ViewClosed;

    #region Windows Form Designer generated code
    . . . 
    #endregion

    #region constructor
    public BaseView()
    {
        InitializeComponent();
        this.Closed += new EventHandler(OnViewClosed);
    }
    #endregion constructor

    #region form eventhandlers
    private void OnViewClosed(object sender, EventArgs ea)
    {
        if (ViewClosed != null)
        {
            ViewClosed(this, ea);
        }
    }

    private void butClose_Click(object sender, EventArgs e)
    {
        this.Close();
    }
    #endregion form eventhandlers

    #region public methods 
    /// <SUMMARY>
    /// Called from the controller to update the View when the Model 
    /// is finished loading the data.
    /// </SUMMARY>
    public void UpdateView(object sender, EventArgs ea)
    {
        BaseModel model = sender as BaseModel;
        if (model == null)
            return;

        DataSet ds = model.QueryModel();
        ContinueUpdateView(ref ds);

        progressBar.Visible = false;
    }

    /// <SUMMARY>
    /// Called from the controller to update the View's progressbar when
    /// the Model is reporting some progress.
    /// </SUMMARY>
    public void UpdateProgress(object sender, int percent)
    {
        progressBar.Visible = true;

        if (percent >= 0 && percent <= 100)
        {
            progressBar.Value = percent;
        }
        else
        {
            progressBar.Value = 0;
        }
    }
    #endregion public methods

    #region virtual methods
    /// <SUMMARY>
    /// This method could be declared as abstract (and then the whole 
    /// BaseView class as an abstract class) but then we lose the ability to edit the
    /// form in the form editor.
    /// </SUMMARY>
    /// <param name="ds"></param>
    protected virtual void ContinueUpdateView(ref DataSet ds)
    {
    }
    #endregion virtual methods
}

The actual implementation of the View overrides the ContinueUpdateView to visually show the data of the model.

Role of the View: Queries the Model for Data and Presents that Data to the User

What glues this whole thing together is the controller. The controller instantiates the model and the view and subscribes them for the events exposed by the other. There is also an abstract base class for different kinds of controllers:

public abstract class BaseController
{
    protected abstract void SubscribeControllerToView();
    protected abstract void SubsctribeModelToView();
}

As the code shows, each controller has to make sure that the view’s events are catched and propagated by the controller to the view (SubscribeControllerToView) and vice versa the model events catched and propagated to the view (SubscribeModelToView).
For instantiating the views (the reports in this case), we just need to instantiate the proper controller like:

RptController controller = new RptController(RptControllerType.List);

The controller knows by the parameter passed in to the constructor as to which view and model to use to create the report.

8. Conclusions

I think if the MVC pattern is used properly, it can speed up development time and also simplify the code complexity by separating the UI layer from the data layer.

Please keep in mind that this article is the first that I have ever written, so if some of the sentences seem to be long and unclear, it is my fault.;)

9. References

10. History

  • 8th January, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Zoltan Balazs
Software Developer
Romania Romania
Member
I've did some programming for Macintosh a few years back and working with Microsoft technologies since then.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: Issue Closing the form during processingmemberZoltan Balazs31 Oct '07 - 6:03 
I tried both the release and the debug version. None of them raises exceptions if I close any of the forms during processing.
 

GeneralRe: Issue Closing the form during processingmemberZGers31 Oct '07 - 22:02 
I am able to reproduce the error by closing the form down (View_List_Income.cs) with the window control panel using the (X).
 
The rptController on method OnModel_ModelProgress throws the following error:
 
Cannot access a disposed object.
Object name: 'View_List_Income'.
 
Can you confirm this. I have migrated solution over to 2005 (Ver 8).
GeneralRe: Issue Closing the form during processingmemberZoltan Balazs31 Oct '07 - 23:44 
I still can't reproduce using VS2003, but in VS2005 I can reproduce it from time to time.
I guess it's an issue with the threading. I have yet to solve this.
Thanks for pointing this out!
 

GeneralNice work!!!memberhav29a2 Oct '07 - 4:26 
It is very clear for me, very nice!!!
Alejandro Varela Big Grin | :-D
 
Ganar o perder es cosa de dioses, a los hombres, solo nos queda luchar...

GeneralRe: Nice work!!!memberZoltan Balazs2 Oct '07 - 4:34 
I'm glad you liked Wink | ;)
 

GeneralSuggestopns - Better implementationmembernji7823 Apr '07 - 10:11 
You should just do away with the RptControllerType Enum and pass the objects directly into the constructor as follows:
 
<font color="blue">public</font> RptController(<font color="teal">BaseView</font> view, <font color="teal">BaseModel</font> model)
{
	<font color="blue">this</font>._view = view;
	<font color="blue">this</font>._model = model;
    
	<font color="firebrick">SubscribeControllerToView();</font>
	<font color="firebrick">SubsctribeModelToView();</font>
			
	<font color="blue">this</font>._model.GenerateReport();  
	<font color="blue">this</font>._view.Show();  
}
 
The two virtual methods should be changed because you should not call virtual methods from a base constructor. Do a more generic wiring up of events that cannot be overriden.
 

GeneralA couple of minor suggestions for improvement regarding event handlingmemberZoodor10 Feb '07 - 5:47 
First off, nice article. It explains the basic concept of MVC without getting too complex, like so many examples I see tend to Smile | :)
 
I just have a couple of points regarding standard Microsoft guidelines on event handling:
 
1) Instead of listening to the class' own event, the On method should be overridden.
 
i.e. in the BaseView constructor, "this.Closed += new EventHandler(OnViewClosed);" should be replaced with a method override of OnClosed():
 
protected override OnClosed(EventArgs e)
{
OnViewClosed( EventArgs.Empty );
 
base.OnClosed( e );
}
 
2) Event raising methods should be called On rather than Fire_ (which you have done correctly in the ViewClosed event in BaseView, but not for the events in BaseModel).
 
2) In event raising methods, the event should be copied before checking for null (to prevent a race condtion where the last event handler for the event could be detached between the null check and the raising of the event) and also shouldn't take the sender as an argument (as it is always the instance of the class raising the event):
 
private void OnViewClosed(EventArgs e)
{
EventHandler handler = this.ViewClosed;
if ( handler != null )
{
handler( this, e );
}
}
 
Sorry if these seem nitpicky, but if people are going to see and use this code to learn from, it's best that they adhere to Microsoft's best practises Smile | :)
GeneralRe: A couple of minor suggestions for improvement regarding event handlingmemberZoltan Balazs11 Feb '07 - 21:33 
Thanks for the comments.
 
1) I don't see why overriding the OnClosed is more suitable than listening to the event itself.
It always seemed to me that this way is more clear.
2) You're right here
3) and I guess here too.
 
Could you point us to the url where you got this info?
As time permits I will correct the code and upload it.
 
company, work and everything else @ netis

GeneralRe: A couple of minor suggestions for improvement regarding event handlingmemberZoodor13 Feb '07 - 2:41 
> 1) I don't see why overriding the OnClosed is more suitable than listening to the event itself.
 
I think the main reason is so that you have better control over when your code executes relative to other event handlers that may also be attached to the event.
 
There are many pages on microsoft's website regarding events, but one page is:
 
http://msdn2.microsoft.com/en-us/library/wkzf914z.aspx
 
which contains most of what I originally posted.
QuestionRe: A couple of minor suggestions for improvement regarding event handlingmemberKamil Kwarciak24 Oct '07 - 6:46 
I have one question to third point. How to refresh a view without pushing the model as a sender?
GeneralAnother suggestion..memberalexdresko10 Jan '07 - 8:10 
In your diagram you show the view querying the model and receiving notifications from the model. I think the view should always query the presenter to receive the model and the presenter should receive notifications from the model and alter the view as necessary.
 

I'm not a player, I just code a lot!
Alex Dresko

GeneralRe: Another suggestion..memberBalazs Zoltan10 Jan '07 - 21:03 
alexdresko wrote:
the view querying the model

This is done in the BaseView class:
		public void UpdateView(object sender, EventArgs ea)
		{
			BaseModel model = sender as BaseModel;
			if (model == null)
				return;
 
                        // this is when we query the model (we ask for it's data)
			DataSet ds = model.QueryModel();
                        // and as a response the view updates itself
			ContinueUpdateView(ref ds);
 
alexdresko wrote:
receiving notifications from the model

As in the RptController class:
		protected override void SubsctribeModelToView()
		{
			if (model == null)
				return;
 
			model.ModelChanged += new EventHandler(OnModel_ModelChanged);
			model.ModelProgress += new ProgressEventHandler(OnModel_ModelProgress);
		}
 
                // the controller forwards the notification from the model 
                // to the view (so the view receives notifications from the model!)
		private void OnModel_ModelChanged(object sender, EventArgs ea)
		{
			if (view == null)
				return;
 
			if (view.InvokeRequired)
			{
				view.Invoke(
					new GenericEventHandler(view.UpdateView),
					new object[] {sender, ea});
			}
			else
			{
				view.UpdateView(sender, ea);
			}
		}
As I said earlier the controller stands in the center of this pattern and through it
happens everything.
 

 
company, work and everything else www.netis.ro

GeneralClarification..memberalexdresko10 Jan '07 - 5:16 
Shouldn't "This design decouples the views from the model that generates the test data. "
 
be..
 
"This design decouples the views from the controller (presenter) that generates the test data (the model). "
 
?
 

I'm not a player, I just code a lot!
Alex Dresko

GeneralRe: Clarification..memberBalazs Zoltan10 Jan '07 - 20:53 
No it should not.
The model doesn't know about the view as neither the view has no knowledge about the model.
The controller stands in the center of this design and it delegates the tasks to be performed.
 
company, work and everything else www.netis.ro

GeneralRe: Clarification..memberGreg Cadmes15 Jan '07 - 5:21 
Balazs is correct and follows the MVC guidlines as indicated in M$ patterns 'n' practices (CAB) project.
Nice artical.
 
Greg Cadmes
GeneralRe: Clarification..memberBalazs Zoltan15 Jan '07 - 5:50 
Thanks Smile | :)
 
company, work and everything else www.netis.ro

QuestionController not updating views?memberRobinLetMeLogin9 Jan '07 - 1:17 
Hi, nice article, but Im confused. Im new to MVC and trying to learn it. You quoted that "Whenever the model's data changes, the model notifies views that depend on it." This is a very important aspect of this design pattern. Yet in your version, this doesnt happen. The graph shows different data to the spread sheet. Opening the spread sheet grid form or the graph more than once and different data is shown everytime. This is confusing.
 
I want to know how the views are updated to show the same data. So if data is changed in one view, shouldnt the other view then be updated to reflect this change?

AnswerRe: Controller not updating views?memberBalazs Zoltan9 Jan '07 - 1:40 
If you look closer when you click on the 'Show list' or 'Show graph' buttons the following code gets executed:
 
RptController controller = new RptController(the RptControllerType you selected);
 
Each time a new controller gets instanciated a new model and a new view is created. The data differs every time because of the Model_Income.GenerateIncome method uses random data to fill the datatable.
 
RobinLetMeLogin wrote:
"Whenever the model's data changes, the model notifies views that depend on it."

When the model is finished generating the data it invokes the Fire_ModelChanged method (in Model_Income.GenerateReport). This fires the event that notifies the view that the model's data has changed.
 
Because in this example there are for each controller only one model and one view associated the graph and the list shows different data every time.
 
The demo can be modified to instanciate 2 separate views in the RptController code if this is what you want. All you need to do is add a second BaseView data member to the RptController class and handle it like the original view.
 
As I intended the article was to show how the same Model code (that generates the random numbers) could be used by multiple views.
 
I hope this helps you Wink | ;)
 
company, work and everything else www.netis.ro

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 8 Jan 2007
Article Copyright 2007 by Zoltan Balazs
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid