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   
Generalgood first articlememberDonsw24 Jul '09 - 15:44 
well done. good first
 
cheers,
Donsw
My Recent Article : Backup of Data files - Full and Incremental

GeneralVery Good~memberkyokof30 Mar '09 - 3:55 
Thanks for your good article
QuestionRefactoringmemberpaheli23 Dec '08 - 20:31 
hi i have developed one application. but i want to Refactor it by using MVC pattern. so in new application business layer is replace with Model ? and what abt utility classes ?
GeneralDiagrammemberMuhammad Azam10 Nov '08 - 20:49 
A very nice and clean article and i really appreciate your work (Y)
 
To me, the diagram shown in this article does not depict what is in the sample code.
I am talking about the "Change Notification Link" between the Model and View.
 
In the sample code its the controller which is subscribing to the model for any data changes
and Model is senfing notifications to the controller which updates the View
 
But the diagram shows that its the view which is subscribing to ModelChange notifications
and is getting update directly from model.
 
can you please explain this to me.
GeneralGood articlememberN a v a n e e t h17 Apr '08 - 2:28 
Hi,
 
Thanks for this good article. It explains MVC well. But I have a small doubt, I am wondering why you used ref for passing DataSet instance ? Since data set is a reference type, I guess ref is not required. I know this is not an issue, but just wondering. Smile | :)
 
Keep up the good work
 
All C# applications should call Application.Quit(); in the beginning to avoid any .NET problems.- Unclyclopedia

How to use google | Ask smart questions

GeneralRe: Good articlememberZoltan Balazs17 Apr '08 - 8:09 
Yes, you are right in this case ref is not required.
However if the ContinueUpdateView() method would modify the ds member directly (and not the data in the dataset) then it would be a use for ref. (see this in article[^] reference types passed by reference (point 7.)).
 
Thanks for pointing out this, I will update the article.
 

GeneralRe: Good articlememberN a v a n e e t h17 Apr '08 - 18:16 
Zoltan Balazs wrote:
However if the ContinueUpdateView() method would modify the ds member directly (and not the data in the dataset) then it would be a use for ref. (see this in article[^] reference types passed by reference (point 7.)).

 
WOW ! It's really a valid point. Thanks for that URL.
 
All C# applications should call Application.Quit(); in the beginning to avoid any .NET problems.- Unclyclopedia

How to use google | Ask smart questions

GeneralNice articlemembermartin_hughes13 Apr '08 - 8:10 
Zoltan,
 
Thanks for this - it's a really helpful example of MVC. I have a couple of questions, however:
 
1) You use abstract classes - would there be any gain/loss by using interfaces?
2) You say it's not suitable for smaller applications, is there a pattern more suited to these?
 
Cheers,
 
Martin.
GeneralRe: Nice articlememberZoltan Balazs13 Apr '08 - 21:31 
martin_hughes wrote:
1) You use abstract classes - would there be any gain/loss by using interfaces?

 
That depends on your needs. In C# multiple inheritance is allowed only for interfaces, so if you need
to derive your classes from another base class you may have to use interfaces for defining the controller and the model.
 
The BaseController is an abstract class because I didn't want to expose the subscribe methods to outside (interface members are always public). So in this case there wouldn't be any point in using interfaces.
 
The BaseModel abstract class contains data members and the event firing protected helper methods.
Here you may define an interface IModel that defines the events and the GenerateReport method.
However I think that BaseModel is better of as an abstract class because I can define the helper methods here instead in each of the classes that would implement IModel.
 
The BaseView class should be implemented as an abstract class also but then you would lose design time support for the form. The abstract class would reflect that this class is not meant to be instanciated.
 
martin_hughes wrote:
2) You say it's not suitable for smaller applications, is there a pattern more suited to these?

 
If you have a small project with a couple of windows presenting similar reports it may be a quicker solution to just implement in a class everything (the view and the model - the logic that gets the data). Using design patterns means more complex - harder to understand solutions, but in the long term easier to maintain solutions.
 

GeneralIssue Closing the form during processingmemberZGers31 Oct '07 - 5:35 
It appears that when you close the form during processing the disposed controls raise an issue.
 
Anyway great implementation of the MVC pattern.
 
Ciao
Zane Gers
 
zane.gers@webafrica.org.za
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

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

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