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

Model View Controller (MVC) Using C#, Delegates and Events in .NET

By , 28 May 2002
 

Introduction

During my third year at the Fontys University in Holland I bumped into a concept called Model-View-Controller. We created a small test project containing the MVC pattern and incorporated different data structures to get experienced with different programming techniques. I embraced the power of patterns and filled my days and nights reading material produced by the Gang of Four. While I was fooling around with .NET during my traineeship at KSE group BV in Holland I decided to experiment with different patterns like MVC, Singleton, Factory etc. This article explains the MVC pattern's basics and clears the big implementation question up with a decent practical example written in my favorite programming language, C#.

Model View Controller

The actual pattern employed in this example, is Model View Controller (MVC). The Windows Forms component will act as the View. There's a book class which represents the Model. The actual Controller logic isn't separated from the View/Model because of my primary design goals. I will publish an extensive MVC-system in the near future which will be reusable for all kind of .NET applications.

Delegates

A delegate is a type-safe method reference. With usage of delegates, programs can dynamically call methods at runtime. In contrast to our MVC pattern delegates provide a solid infrastructure to implant the publish-subscribe notification pattern. For those who have experience with Java, I'm referring to the Observer/Observable registration functionality witch the JDK 1.3 provides for exploiting the MVC pattern in the Java environment.

Events

An event is common in modern programming languages. The basic usage of events is indicating certain user-defined occurrences inside your program. Events are used to notify certain listeners during the program lifetime. A very good example is button clicks or other dynamic actions in your programs. Events are again powered by the publish/subscriber pattern.

The model

While coding this basic example in order to explore MVC pattern I thought of books obviously. Below displayed is a basic book class implementation. This Book class contains a private member called bookPrice. The basic functionality I want to provide for all object who wish to instantiate my book class is an actual book price, in the case that the price changes, my views will update their views in order to display the actual book price. The idea is simple so lets move on… see the comments in the code.

using System;
namespace MVC
{
    public class Book
    {
        // declare a delegate for the bookpricechanged event
        public delegate void BookPriceChangedHandler(objectsender,
        BookPriceChangedEventArgs e);
        
        // declare the bookpricechanged event using the bookpricechangeddelegate
        public event BookPriceChangedHandlerBookPriceChanged;
        
        // instance variable for book price
        object _bookPrice;
        
        // property for book price
        public object BookPrice
        {
            set
            {
                // set the instance variable
                _bookPrice=value;
                
                // the price changed so fire the event!
                OnBookPriceChanged();
            }
        }
        
        // method to fire price canged event delegate with proper name
        // this is the method our observers should be implenting!
        protected void OnBookPriceChanged()
        {
            BookPriceChanged(this, new BookPriceChangedEventArgs(_bookPrice));
        }
    }
}

So what happened here; we'd structured the basic layout for the book class. Notice the declared delegate and event BookPriceChangedHandler and BookPriceChanged. The delegate encapsulates the BookPriceChanged method. Later we'll see why, but for know its enough to know that the BookPriceChanged method will be implemented in the object that wishes to observe this book class. As you can see, the method gets called with the BookPriceChangedEventArgs. This argument object contains the public properties the book object encapsulates. This is just the standard way for transporting multiple parameters towards methods. Kind of basic programming, so this should be self explanatory. Lets take a brief look at the BookPriceChangedEventArgs class.

namespace MVC
{
    // specialized event class for the bookpricechanged event
    public class BookPriceChangedEventArgs : EventArgs
    {
        // instance variable to store the book price
        private object _bookPrice;

        // constructor that sets book price
        public BookPriceChangedEventArgs(objectbookPrice)
        {
            _bookPrice=bookPrice;
        }

        // public property for the book price
        public object BookPrice
        {
            get
            {
                return _bookPrice;
            }
        }
    }
}

Maybe you noticed that I used the object (like in Java the "any") instead of string declaration for my price value. The main reason for this is that while creating this model I didn't know what kind of GUI representation I wished to use to show my book price value. In case you wish to add more members to the Book class, for instance a name, author, you should extend this EventArgs class. I would consider giving the class a more abstract name like BookChanged.

The view

Now our model is functional so let's move on to the View. Below is a listing of the view object BookView which subscribes to the book model using a delegate. This way the observer gets notified when the price change events is thrown in the model (the observable). You could pass the private book object through reference to multiple views (think of those smart Adobe Photoshop panels, which are views on the underlying model). In this demo I created a simple two-view system making use of a textbox and a basic label to visualize the book model.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
 
namespace MVC
{
    public class BookView : System.Windows.Forms.Form
    {
        private System.ComponentModel.Container components = null;
 
        // Declare our delagate and book object
        private Book.BookPriceChangedHandler bookDelegate;
        private Book book;
 
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.GroupBox groupBox2;
        private System.Windows.Forms.Label label1;
 
        public BookView()
        {
            InitializeComponent();
 
            //create new book instance
            book=new Book();
   
            // create a new delegate, instance and bind it
            // to the observer's bookpricechanged method
            bookDelegate = new Book.BookPriceChangedHandler(this.BookPriceChanged);    
            //add the delegate to the event
            book.BookPriceChanged += bookDelegate;
        }
 
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                     components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
 
        #region Windows Form Designer generated code
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.label1 = new System.Windows.Forms.Label();
            this.groupBox1.SuspendLayout();
            this.groupBox2.SuspendLayout();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(120, 16);
            this.button1.Name = "button1";
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(8, 16);
            this.textBox1.Name = "textBox1";
            this.textBox1.TabIndex = 1;
            this.textBox1.Text = "";
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.AddRange(new System.Windows.Forms.Control[] {
            this.textBox2});
            this.groupBox1.Location = new System.Drawing.Point(8, 104);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(120, 64);
            this.groupBox1.TabIndex = 2;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "View 1";
            // 
            // textBox2
            // 
            this.textBox2.Location = new System.Drawing.Point(8, 32);
            this.textBox2.Name = "textBox2";
            this.textBox2.TabIndex = 0;
            this.textBox2.Text = "";
            // 
            // groupBox2
            // 
            this.groupBox2.Controls.AddRange(new System.Windows.Forms.Control[] {
            this.label1});
            this.groupBox2.Location = new System.Drawing.Point(8, 176);
            this.groupBox2.Name = "groupBox2";
            this.groupBox2.Size = new System.Drawing.Size(120, 64);
            this.groupBox2.TabIndex = 3;
            this.groupBox2.TabStop = false;
            this.groupBox2.Text = "View 2";
            // 
            // label1
            // 
            this.label1.Location = new System.Drawing.Point(8, 32);
            this.label1.Name = "label1";
            this.label1.TabIndex = 0;
            // 
            // BookView
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.AddRange(new System.Windows.Forms.Control[] {
            this.groupBox2,
            this.groupBox1,
            this.textBox1,
            this.button1});
            this.Name = "BookView";
            this.Text = "BookView";
            this.groupBox1.ResumeLayout(false);
            this.groupBox2.ResumeLayout(false);
            this.ResumeLayout(false);
 
        }
        #endregion
 
        [STAThread]
        static void Main() 
        {
            Application.Run(new BookView());
        }
 
        public void BookPriceChanged(object sender, MVC.BookPriceChangedEventArgs e) 
        {
            // update bookprice views
            object bookPrice;
            bookPrice = e.BookPrice;         
            this.textBox2.Text = bookPrice.ToString();
            this.label1.Text = bookPrice.ToString();
        }
 
        private void button1_Click(object sender, System.EventArgs e)
        {
            // change book price
            book.BookPrice = this.textBox1.Text;
        }
    }
}

As soon you make changes to the book price, the BookPriceChanged will be triggered in the model. Because the view is registered to the OnBookPriceChanged method, the BookPricechanged will be invoked from the model. The View (observer) implements the BookPriceChanged method so the views get updated according to the rules as specified in the view upon the book model. Confused? Then start toying with this code because you now know the basics concerning a simple MVC! The book class code is reusable for whatever functionality you wish to provide to the outside world from out your model. Have fun!

Conclusion

I'm sure I've explained the MVC mechanism in the .NET world using C# extensively. In my opinion .NET is a fantastic environment for both starting coders and more experienced coders. You'll need some basic knowledge concerning object oriented programming to make a jump start into the .NET Framework. I enjoyed writing this article and hope it was a useful addition for those who read it.

Who am I?

I am a dedicated programmer combining software technology with web technology. I'm currently working part-time as a software engineer at Connexx Communications and studying Information Technology at the Fontys University in Holland. In my spare time I enjoy fooling around with new technology like .NET and coding for a Half Life mod named Hostile Intent.

You have a royalty-free right to use, modify, reproduce and distribute the Sample Application Files (and/or any modified version) in any way you find useful, provided that you agree that P.Gielens has no warranty, obligations or liability for any Sample Application Files.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Paul Gielens
Web Developer
Netherlands Netherlands
Member
No Biography provided

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   
GeneralMy vote of 1memberBrian C Hart16 Apr '12 - 11:38 
This article is awful. It's like the author just copied and pasted the documentation on what a delegate and event is. It does not demonstrate an understanding of the MVC pattern.
Buga few 'typo' errors ?membermwolmstr13 Jan '12 - 4:15 
A few typo errors perhaps from pasting article?
 
In the class Book, 2 occurances of names connected erroneously:
 
#1 in statement: public delegate void BookPriceChangedHandler(objectsender
objectsender should split into object sender
 

#2 in statement: public event BookPriceChangedHandlerBookPriceChanged
event name should be split into BookPriceChangedHandler BookPriceChanged
 
some people find this right away and others just give up, but I thought it should be mentioned
so we will have good working source code examples.
GeneralMy vote of 1memberzenwalker198517 Oct '11 - 20:33 
Not an MVC
GeneralMy vote of 1memberBoutemine Oualid13 Mar '10 - 5:16 
This is not MVC!
GeneralMVC architecture in .net Windows formmemberMember 343239818 Nov '09 - 19:52 
Hi,
I have to shift MFC project into C# .net windows applications(windows forms).
I have used MVC architecture in MFC( Model and Controller part is in DOC itself.).
 
I am little confused about use of MVC architecture and one more thing I want to tell you that, Now I want to make proper MVC architecture in C#. windows application i.e creating separate Model-view-architecture design.
But I never used in C# .net (windows forms). Now I am learning to it.
So I need help about it. If you know any article, link or forum, please share it.
GeneralMy vote of 1memberandruski_uk7 Jul '09 - 4:34 
Not MVC.
GeneralMy vote of 1memberMember 31229134 Jul '09 - 8:32 
It is not MVC
GeneralMy vote of 1memberBlueNexus1 Jul '09 - 10:52 
Not the MVC pattern
GeneralMy vote of 1memberMember 92033321 Jun '09 - 20:20 
Not MVC!
QuestionNot MVCmemberMember 92033321 Jun '09 - 20:20 
Doesn't seem like a very good example of MVC. Maybe you should change the title, delete article or change the content? This is causing confusion.
AnswerRe: Not MVCmemberJason Jakob9 Nov '09 - 8:59 
Agreed. The best simple MVC example with a true controller is here:
 
http://www.primaryobjects.com/CMS/Article82.aspx
GeneralMy vote of 1memberitzasifmd17 Jun '09 - 20:02 
How come view directly instantiate model which is book here.
GeneralMy vote of 1membersdiemer7 May '09 - 16:37 
The article claims to be MVC but there is no use of a controller
GeneralMy vote of 1membersadekdm4 Feb '09 - 8:27 
Has nothing to do with MVC.
GeneralMy vote of 1memberJoey Chömpff26 Jan '09 - 7:57 
This is NOT the MVC-pattern
GeneralNot MVC - No Controller (again)memberloudenvier8 Mar '08 - 22:04 
Hi, this is not the MVC pattern, this is the observer pattern!
 
In fact, this is one of the clearest explanations of how the observer pattern can be implemented in C# using events and delegates... The only problem is that you called it MVC instead.
 
No, .Net is not the Controller for you... It does not provide what a controller should be doing... It only makes it easy for you to notify listeners, but a controller is responsible for changing the model, and that is done directly in your code, intermingled with the view code, etc.
 
You may wish to read this article http://msdn2.microsoft.com/en-us/library/ms954621.aspx[^]
 
It bears many similarities to yours, but it correctly calls itself and observer pattern, not MVC.
QuestionWhere to instantiate the model?memberBartosz Bien18 Oct '06 - 4:05 
In your example, the model (Book) is instantiated in a view (a form). Where would you create it if there were multiple views (forms) present?
 

GeneralGoodmemberAhmed Erarslan22 Nov '05 - 0:21 
Conragulation It's Perfect SolutionSmile | :)
 
Ahmed Erarslan
MCAD,MCDBA,MCP

MCSD.NET
GeneralRe: Goodmemberitzasifmd17 Jun '09 - 20:07 
This is Perfect example of MVC? ohh man you are MCP and bla bla bla
Generalagain ... the controlermemberspidermike13 Nov '05 - 22:14 
sorry, but for me it isn't clear what exactly is the controler part in the code. it was mentioned, that the class bookpricechangedeventargs is the controler in this example. but then i read that it is in the view-code?
 
can anyone explain it to me? thx!
GeneralYou missed the Sub-Heading!memberBalamurali Balaji11 Jul '05 - 20:11 
Hi paul,
 
Just now, I come across your nice article on MVC and I also looked into various comments on missing controller class. Actually, you have it in your code. The class BookPriceChangedEventArgs is a controller class, but missed to point out that it is a controller. It represents the translation of interactions with the view into actions to be performed by the user.
 
regards,
good luck
 
(bala)
WWW.DotnetSpider.COM
GeneralSource CodememberAlan James1 Mar '05 - 5:56 

Paul
 
Thanks for posting an interesting and useful article.
 
I do not see a link on the article to download the source code and project solution. Is this publically available?
 
Thanks
 
Alan
GeneralRe: Source CodesussAnonymous9 May '05 - 8:32 
If you create a new Windows Form project in Visual Studio, you can just cut and paste on top of that and it should work fine. Did for me. cheers.
GeneralRe: Source CodememberRoeldeB16 Sep '08 - 1:33 
It does not work for me. I got 4 errors. 3 of them I could fix myself except for this one:
Error 1 Invalid token 'object' in class, struct, or interface member declaration \Book.cs 17 9 MVCExample
 

Edit: I see the problem now:
The line:
public event BookPriceChangedHandlerBookPriceChanged;
 
Must be:
 
public event BookPriceChangedHandler BookPriceChanged;
 
There is a space missing.
Also sometimes the word object is missing in some function / delegate declarations.
GeneralUser ControlsmemberMol20016 Dec '03 - 5:12 
I use a pattern which is just like yours. I haven't seperated the controller from the main Form (no need since this is actually my controller). I use UserControls to implement the real views and the Form function as a container (and the controller). This is just a first shot at an application. I just wanted to make it pretty simple to do the refactoring later on, but can't see the need for this yet. Then I would seperate out the controller from the main form wich should be very easy.
 
You will need to handle the broadcast of messages inside the different Views. I have done a simple test for this.Visible && this.Enable before the view is acting on the event. Also there might be events that you know should be ignored (i.e. the events you recieve if you repopulate a combobox).
 
Also you might get some trouble with timing. I sometimes go into this problem:
 
1.) Data is updated
2.) Need to prepare view
3.) Change data event fired
 
Hench, I need to prepare the View before the view can recieve the broadcast messsage.
 
This is a rather small problem, but perhaps I'm doing something strange or could handle this in some other way?

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 29 May 2002
Article Copyright 2002 by Paul Gielens
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid