Overview
Model-View-Controller is a pattern used extensively throughout the industry to decouple the presentation layer from the business logic layer. Basically the idea is that the presentation layer should not talk directly to the actual data or Model.
Instead it uses a Controller to change the model in a system acceptable manner, and a View to subscribe to, for updates to the model.
Ideally the implementation will avoid the Presentation Layer (anything derived from System.Windows.Forms e.g. Forms and Controls) from seeing anything in the Data Layer, i.e. the Model. This is eased by the use of a Factory.
Patterns in Use
MVC - Well MVC is itself a pattern as described in Pattern Orientated Software Architecture Volume 1: A system of Patterns.
Publisher-Subscriber - as described in Design Patterns is used in order to update the views
Factory Method - This implementation uses a factory object to ensure that the Form, View, Controller heirarchy is correctly (You have to love Generics)
Implementation
This implementation works by providing base classes that you inherit from. It works quickly in .Net v1.1. It does not however answer all of the issues regarding MVC. See Issues of implementing MVC (to come)
Example Code
M
using System;
using System.Collections;
namespace EventMon.MVC
{
/// <summary>
/// Summary description for Model.
/// </summary>
public class Model
{
private ArrayList _views = new ArrayList();
/// <summary>
/// Construct a model that handles <see cref="View"/> synchronisation (see <see cref="OnChange"/>)
/// </summary>
public Model()
{
}
/// <summary>
/// Adds a <see cref="View"/>that will be updated when the <see cref="Model"/> changes
/// </summary>
public void Watch(View view)
{
_views.Add(view);
}
/// <summary>
/// Drops a <see cref="View"/> from the update list
/// </summary>
/// <param name="view"></param>
public void StopWatch(View view)
{
_views.Remove(view);
}
/// <summary>
/// Called by derived classes when a change is required to be sent to all <see cref="Views"/>
/// </summary>
public void OnChange()
{
foreach(View view in _views)
{
view.ModelChanged();
}
}
}
}
V
using System;
namespace EventMon.MVC
{
/// <summary>
/// Simple means of getting updates from a <see cref="Model"/>
/// </summary>
public abstract class View : IDisposable
{
/// <summary>
/// The <see cref="Model"/> to be notified of changes in
/// </summary>
private Model _model = null;
/// <summary>
/// Constructs a new <see cref="View"/> of the given <see cref="Model"/>
/// </summary>
public View(Model model)
{
_model = model;
model.Watch(this);
}
/// <summary>
/// Detaches f=this <see cref="View"/> from the <see cref="Model"/>
/// </summary>
public void Close()
{
if (_model != null)
{
_model.StopWatch(this);
_model = null;
//Use above closure to ensure only one call
CloseView();
}
}
/// <summary>
/// Implement this to perform any inherited Cleanup
/// </summary>
public abstract void CloseView();
/// <summary>
/// Implement this to perform the actions that must be performed by a
/// <see cref="View"/> when a <see cref="Model"/> changes.
/// Ideally you want to implement an asynchronous refresh mechanism to
/// avoid one refresh for every update to the model. (See
/// <see cref="Control.Invalidate"/> / <see cref="Control.OnPaint"/>)
/// </summary>
/// <param name="model">The <see cref="Model"/>that has changed.</param>
public abstract void ModelChanged();
/// <summary>
/// Gets the <see cref="Model"/> that this is registered with
/// </summary>
public Model Model
{
get
{
return _model;
}
}
#region IDisposable Members
public void Dispose()
{
Close();
}
#endregion
}
}
C
using System;
namespace EventMon.MVC
{
/// <summary>
/// Summary description for Controller.
/// </summary>
public abstract class Controller
{
#region fields
/// <summary>
/// The <see cref="Model"/> to be notified of changes in
/// </summary>
private Model _model = null;
#endregion
#region Constructor
/// <summary>
/// Constructs a new <see cref="Controller"/> of the given <see cref="Model"/>
/// </summary>
public Controller(Model model)
{
_model = model;
}
#endregion
#region Close pattern
/// <summary>
/// Detaches this <see cref="Controller"/> from the <see cref="Model"/>
/// </summary>
public void Close()
{
if (_model != null)
{
EndChange();
}
}
/// <summary>
/// Implement this to perform any inherited Cleanup
/// </summary>
public abstract void CloseController();
#region IDisposable Members
public void Dispose()
{
Close();
}
#endregion
#endregion
#region Update collection
private bool _isChanging = false;
protected bool IsChanging
{
get
{
return _isChanging;
}
}
public void BeginChange()
{
_isChanging = true;
}
public void EndChange()
{
if (_isChanging)
{
_isChanging = false;
_model.OnChange();
}
}
#endregion
#region Properties
protected Model Model
{
get
{
return _model;
}
}
#endregion
}
}
Example Factory
using System;
using EventMon.BusinessObjects;
namespace EventMon.Presentation
{
/// <summary>
/// Summary description for FilterFactory.
/// </summary>
public class FilterFactory
{
private FilterModel _model;
private FilterViewForm _form;
private FilterView _view;
private FilterController _controller;
public FilterFactory(FilterModel model)
{
_model = model;
}
public void Build()
{
_controller = new FilterController(_model);
_form = new FilterViewForm(_controller);
_view = new FilterView(_model, _form);
}
#region Properties
public FilterViewForm Form
{
get
{
return _form;
}
}
public FilterView View
{
get
{
return _view;
}
}
public FilterController Controller
{
get
{
return _controller;
}
}
#endregion
}
}