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
{
public delegate void BookPriceChangedHandler(objectsender,
BookPriceChangedEventArgs e);
public event BookPriceChangedHandlerBookPriceChanged;
object _bookPrice;
public object BookPrice
{
set
{
_bookPrice=value;
OnBookPriceChanged();
}
}
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
{
public class BookPriceChangedEventArgs : EventArgs
{
private object _bookPrice;
public BookPriceChangedEventArgs(objectbookPrice)
{
_bookPrice=bookPrice;
}
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;
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();
book=new Book();
bookDelegate = new Book.BookPriceChangedHandler(this.BookPriceChanged);
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();
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);
this.textBox1.Location = new System.Drawing.Point(8, 16);
this.textBox1.Name = "textBox1";
this.textBox1.TabIndex = 1;
this.textBox1.Text = "";
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";
this.textBox2.Location = new System.Drawing.Point(8, 32);
this.textBox2.Name = "textBox2";
this.textBox2.TabIndex = 0;
this.textBox2.Text = "";
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";
this.label1.Location = new System.Drawing.Point(8, 32);
this.label1.Name = "label1";
this.label1.TabIndex = 0;
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)
{
object bookPrice;
bookPrice = e.BookPrice;
this.textBox2.Text = bookPrice.ToString();
this.label1.Text = bookPrice.ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.