(c) 2005 Marc Clifton All Rights Reserved.
Introduction
The Model-View-Controller (MVC) design pattern is a powerful way to separate
the concerns of the user interface, the data model, and the state management of
both. Events are used to achieve this decoupling. Both the model and view are
well suited for declarative programming. The model manages the data, being a
collection of fields with a known type, and the view is a representation of the
user interface as expressed by a collection of controls. The controller is best
constructed in code, as it contains the logic for responding to the events fired
by the view and adjusting the model's state.
In a classic MVC pattern, the view does not directly change the model's state
(as in, field values managed by the model). Instead, it fires events to the
controller. The controller contains the logic to decide how the model state
changes and informs the model of the appropriate state change with a direct
function call. The model, upon processing the state change, fires an event which
is received by the view(s). The view adjusts the state of the UI to reflect the
state change of the model.
When data binding is employed, the model and view have a direct communication
path that is managed by the underlying framework rather than the application. If
the view changes, the field's value in the model is automatically updated.
Conversely, if the field's value in the model changes, the view is automatically
updated. The controller never intervenes unless the Binding class'
Parse event is handled. However, this event is intended to provide
custom unformatting of displayed values rather than state management, so it's
not the appropriate use of the event.
A Working Example
I am going to illustrate the MVC pattern using MyXaml as the declarative
syntax. A simple example is a date-range picker, which is a common enough dialog
used to select the date ranges of reports:

Declaratively Creating The Model
The model for this UI manages the following fields:
- start date
- end date
- selected default date range option
- group by index
This is expressed declaratively by constructing a container in the
markup:
<MxContainer Name="DateSelection">
<MxProperties>
<MxProperty Name="StartDate" Type="DateTime"/>
<MxProperty Name="EndDate" Type="DateTime"/>
<MxProperty Name="Option" Type="int"/>
<MxProperty Name="GroupBy" Type="int"/>
</MxProperties>
</MxContainer>
Now, we have a problem. Data binding works with fields only through
corresponding property "get" and "set" methods. We can't abstract this concept
with data binding, so instead, we have to generate at runtime the code for the
class that represents this container and instantiate it. The
MxContainer class, when instantiated by the MyXaml parser and all
the parsing of the class is complete, does exactly this. The following code is
auto-generated:
// MyXaml Runtime Generated Code
using System;
using System.Collections;
using MyXaml.Extensions;
namespace MyXaml.App.Container
{
public class DateSelection : MxDataContainer
{
protected DateTime startDate;
protected DateTime endDate;
protected int option;
protected int groupBy;
public DateTime StartDate
{
get {return startDate;}
set {startDate=value;}
}
public DateTime EndDate
{
get {return endDate;}
set {endDate=value;}
}
public int Option
{
get {return option;}
set {option=value;}
}
public int GroupBy
{
get {return groupBy;}
set {groupBy=value;}
}
}
}
Initial values must also be established if they are not defined in the
MxProperty element, otherwise the data binding fails for objects
such as DataTime. This also is handled by the
MxContainer class. The resulting instance of the
DateSelection class is put into the parser's object pool. Later on,
I will discuss why the class is derived from MxDataContainer.
Declaratively Creating The UI
The user interface is generated with the typical declarative syntax:
<Style def:Name="RangeStyle">
<StyleProperties>
<PropertyStyle Class="MxRadioButton" Size="80, 20"/>
</StyleProperties>
</Style>
<Controls>
<Label Location="10, 10" Size="220, 15" Text="Select A Date Range"
TextAlign="MiddleCenter"/>
<Label Location="10, 42" Size="70, 15" Text="Start Date:"/>
<Label Location="10, 67" Size="70, 15" Text="End Date:"/>
<DateTimePicker Location="80, 40" Size="100, 20" Format="Short">
<DataBindings>
<MxDataBinding PropertyName="Value" DataSource="DateSelection"
DataMember="StartDate"/>
</DataBindings>
</DateTimePicker>
<DateTimePicker Location="80, 65" Size="100, 20" Format="Short">
<DataBindings>
<MxDataBinding PropertyName="Value" DataSource="DateSelection"
DataMember="EndDate"/>
</DataBindings>
</DateTimePicker>
<GroupBox Location="10, 95" Size="220, 110" Text="Default Options:">
<Controls>
<MxRadioButton Location="10, 20" Size="80, 15" Text="Today">
<DataBindings>
<MxDataBinding PropertyName="Index" DataSource="DateSelection"
DataMember="Option"/>
</DataBindings>
</MxRadioButton>
<MxRadioButton Location="10,40" Text="This week" MxStyle="{RangeStyle}"/>
<MxRadioButton Location="10,60" Text="This month" MxStyle="{RangeStyle}"/>
<MxRadioButton Location="10,80" Text="This year" MxStyle="{RangeStyle}"/>
<MxRadioButton Location="120,20" Text="Yesterday" MxStyle="{RangeStyle}"/>
<MxRadioButton Location="120,40" Text="Last week" MxStyle="{RangeStyle}"/>
<MxRadioButton Location="120,60" Text="Last month" MxStyle="{RangeStyle}"/>
<MxRadioButton Location="120,80" Text="Last year" MxStyle="{RangeStyle}"/>
</Controls>
</GroupBox>
<GroupBox Location="235, 95" Size="100, 110" Text="Group By:">
<Controls>
<MxRadioButton Location="10, 20" Size="70, 20" Text="Hour">
<DataBindings>
<MxDataBinding PropertyName="Index" DataSource="DateSelection"
DataMember="GroupBy"/>
</DataBindings>
</MxRadioButton>
<MxRadioButton Location="10, 40" Size="70, 20" Text="Day"/>
<MxRadioButton Location="10, 60" Size="70, 20" Text="Month"/>
<MxRadioButton Location="10, 80" Size="70, 20" Text="Year"/>
</Controls>
</GroupBox>
<Button def:Name="OKButton" Location="250, 10" Size="80, 25" Text="OK"
FlatStyle="System" Click="OnDlgOK"/>
<Button def:Name="CancelButton" Location="250, 35" Size="80, 25"
Text="Cancel" FlatStyle="System" Click="OnDlgCancel"/>
</Controls>
Data Binding Problems
Unfortunately, data binding comes with its own set of problems, some of which
I am illustrating here.
The first and by far most annoying problem is when using data binding with
regards to a radio button. Radio buttons are unique because, as the programmer,
we're almost always interested in which button in the group was selected. Data
binding only offers us the ability to bind to each property of each individual
button. This means that we would need separate properties in the container, and
this defeats the index-like quality of the radio button. To solve this problem,
the MxRadioButton is a class that extends the
RadioButton functionality by implementing an Index
property. By binding to this property, the MxRadioButton class
intelligently sets the Checked state of the appropriate radio
button in the group.
Second, when the Click event fires on the
RadioButton, the data binding update has not yet occurred! This
means that we have to inspect the value of the control directly, rather than the
property value being managed by the container. Again, to deal with this problem,
the base class of the container, MxDataContainer, forces a transfer
of the data to and from the control when we respectively set and get the value
of the property in the container.
The good thing is, once these fixes are in place, they can be used over and
over again in all of our applications.
Container Problems
The container is generated at runtime, so your compile-time code doesn't know
anything about it. This means that you have to use a separate vehicle for
accessing the properties in the container. Hence the reason for the container's
base class, MxDataContainer. This is a class known at your
application's compile time, and can be used to access the properties of the
runtime compiled container. While awkward, it also has an advantage--the
container's data representation is decoupled from the controller, allowing the
two to vary independently, which has some advantages.
The Controller
In our controller for this UI, we want to do two things:
- When a default date range is selected, automatically adjust the beginning
and ending dates.
- When the user changes the beginning or ending date, "uncheck" all radio
buttons in the default range group.
Handling User Gesture Events
There are a variety of ways that this can be done, but in keeping with the
MVC pattern, we want the view decoupled from the controller. In other words, the
view doesn't call a method in the controller. Ideally, the view should consume
any Control events and provide the controller with a "sanitized"
version of the event. If the controller were to directly consume the
Control's event, then a level of decoupling would be lost. This
would become apparent if the view were to change and the new
Control's event had a different EventArgs signature.
This would break the controller's implementation.
The Event Pool that I've written about in a previous article is a nice way of
managing this requirement. It also has several advantages:
- it documents the producers and consumers of events;
- it instruments the events;
- it provides a safe mechanism for invoking the events, regardless if there
is a consumer or not.
The question then remains, does the view pass along any data to the
controller? We could easily put selection information into the Tag
property of the radio buttons. But we already have a property in the container
that can be used to get the current selection, so it seems more logical (and
simpler) to use the container. The drawback to this is that it assumes that the
property in the container will always have a data binding to the control,
however it is implemented. This decision will have to be left to the programmer,
based on what his/her crystal ball says regarding the possible views that might
be encountered in the future. But, since the Control class, from
which all controls are derived, contains the DataBindings
collection, it's reasonable to say "always use data binding with a property in
the container". That said, we probably don't ever have to worry about whether
the view needs to pass parameters to the controller--all the relevant
information should be in the container model.
Mapping Control Events
As a result of the decisions made regarding handling Control
events, it would be nice if we could just map the Control event to
an appropriate user gesture event managed by the EventPool. Yes,
this seems like an unnecessary step in such a trivial example, but the beauty of
it lies in the fact that, once handled by the EventPool, the event
is instrumented! This means that we now have an automatic trace of user actions
regarding what the guy in QA did, when upon being asked "how did you break it?",
he replies "I dunno". This single feature has reduced QA acceptance time by
two-thirds in projects on which I have worked. So, even for something that seems
like unnecessary complication, there are great benefits.
Because each radio button represents a different state that we want to set in
the model, it makes sense to create a separate event handler for each
Click event from the RadioButton control. Therefore,
we don't need to inspect the Option property of the container and
we avoid a switch statement.
Since the event pool implements multicast delegates, each event managed by
the pool can have more than one subscriber. Again, we can use XML to
declaratively establish the connection between the Control event in
the View, the corresponding EventPool event, and the handler that
subscribes to the event. The ControlEvents collection handles the
mapping of the control event to the corresponding EventPool event.
The Subscribers collection manages the mapping of the
EventPool event to one or more subscribers.
<ev:EventPool def:Name="DateRangeEventPool">
<ev:ControlEvents>
<ev:ControlEvent Control="{rbToday}"
Event="Click" MapTo="SelectToday"/>
<ev:ControlEvent Control="{rbWeek}"
Event="Click" MapTo="SelectThisWeek"/>
<ev:ControlEvent Control="{rbMonth}"
Event="Click" MapTo="SelectThisMonth"/>
<ev:ControlEvent Control="{rbYear}"
Event="Click" MapTo="SelectThisYear"/>
<ev:ControlEvent Control="{rbYesterday}"
Event="Click" MapTo="SelectYesterday"/>
<ev:ControlEvent Control="{rbLastWeek}"
Event="Click" MapTo="SelectLastWeek"/>
<ev:ControlEvent Control="{rbLastMonth}"
Event="Click" MapTo="SelectLastMonth"/>
<ev:ControlEvent Control="{rbLastYear}"
Event="Click" MapTo="SelectLastYear"/>
<ev:ControlEvent Control="{StartDate}"
Event="ValueChanged" MapTo="CustomDate"/>
<ev:ControlEvent Control="{EndDate}"
Event="ValueChanged" MapTo="CustomDate"/>
</ev:ControlEvents>
<ev:Subscribers>
<ev:Subscriber Event="SelectToday" Instance="{DateRangeController}"
Handler="OnSelectToday"/>
<ev:Subscriber Event="SelectThisWeek" Instance="{DateRangeController}"
Handler="OnSelectThisWeek"/>
<ev:Subscriber Event="SelectThisMonth" Instance="{DateRangeController}"
Handler="OnSelectThisMonth"/>
<ev:Subscriber Event="SelectThisYear" Instance="{DateRangeController}"
Handler="OnSelectThisYear"/>
<ev:Subscriber Event="SelectYesterday" Instance="{DateRangeController}"
Handler="OnSelectYesterday"/>
<ev:Subscriber Event="SelectLastWeek" Instance="{DateRangeController}"
Handler="OnSelectLastWeek"/>
<ev:Subscriber Event="SelectLastMonth" Instance="{DateRangeController}"
Handler="OnSelectLastMonth"/>
<ev:Subscriber Event="SelectLastYear" Instance="{DateRangeController}"
Handler="OnSelectLastYear"/>
<ev:Subscriber Event="CustomDate" Instance="{DateRangeController}"
Handler="OnCustomDate"/>
</ev:Subscribers>
</ev:EventPool>
To make this all work, we again rely on runtime code generation to create the
appropriate bridge class. The emitted code looks like this:
// MyXaml Runtime Generated Code
using System;
using System.Collections;
using MyXaml.EventManagement;
using MyXaml.Extensions;
namespace MyXaml.App.ControlEvents
{
public class DateRangeEventPool : IEventPool
{
protected EventPool eventPool;
public EventPool EventPool
{
get {return eventPool;}
set {eventPool=value;}
}
public void Click_SelectToday(object sender, EventArgs e)
{
eventPool.FireEvent("SelectToday");
}
public void Click_SelectThisWeek(object sender, EventArgs e)
{
eventPool.FireEvent("SelectThisWeek");
}
public void Click_SelectThisMonth(object sender, EventArgs e)
{
eventPool.FireEvent("SelectThisMonth");
}
public void Click_SelectThisYear(object sender, EventArgs e)
{
eventPool.FireEvent("SelectThisYear");
}
public void Click_SelectYesterday(object sender, EventArgs e)
{
eventPool.FireEvent("SelectYesterday");
}
public void Click_SelectLastWeek(object sender, EventArgs e)
{
eventPool.FireEvent("SelectLastWeek");
}
public void Click_SelectLastMonth(object sender, EventArgs e)
{
eventPool.FireEvent("SelectLastMonth");
}
public void Click_SelectLastYear(object sender, EventArgs e)
{
eventPool.FireEvent("SelectLastYear");
}
public void ValueChanged_CustomDate(object sender, EventArgs e)
{
eventPool.FireEvent("CustomDate");
}
}
}
At this point, you are probably thinking that Marc has really gone off his
rocker this time! He's managed to create a bunch of time-consuming runtime
generated code that replaces the very simple mechanism in .NET of wiring up an
event to a handler!
And, in a sense, you are correct.
However, let's look at what all of this does:
- I have eliminated the need to, in code, wire up events. OK, MyXaml did
that already.
- All View events are now instrumented. Cool! I can track what I do, and why
the program breaks!
- In .NET, the wiring up of the event requires that the code has both
knowledge of the
Control class and the instance (the Controller)
that handles the event. I have eliminated that coupling. Ahhh! Now, MyXaml did
that already, but the event signature was bound to the Control
type. This is more abstract.
- It creates an architectural pattern that helps me write better code. The
Controller now represents a set of methods that manages the Model state.
Typical implementations embed the model state management as part of the
subclassed Form, at least, if you follow Visual Studio's way of generating
event handlers. The "Microsoft Way" makes it impossible to implement an MVC
pattern, results in costly code rewrites when the View changes, and in
general, bogs down the flexibility of the application.
The Controller implementation is straight forward, which we'll add as in-line
code to the markup so we get a complete stand-alone package (yes, I wrote this
code first as an assembly, debugged it, then copied it into the markup):
<def:Code language="C#">
<reference assembly="System.Drawing.dll"/>
<reference assembly="System.Windows.Forms.dll"/>
<reference assembly="myxaml.dll"/>
<![CDATA[
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using MyXaml;
using MyXaml.Extensions;
public class DateRangeController
{
protected Parser parser;
protected MxDataContainer dateSelection;
protected TimeSpan oneDay;
public DateRangeController()
{
parser=Parser.CurrentInstance;
parser.AddReference("DateRangeController", this);
oneDay=new TimeSpan(1, 0, 0, 0);
}
public void DateSelectionLoaded(object sender, EventArgs e)
{
dateSelection=(MxDataContainer)parser.GetReference("DateSelection");
}
public void OnSelectToday(object sender, EventArgs e)
{
dateSelection.SetValue("StartDate", DateTime.Now);
dateSelection.SetValue("EndDate", DateTime.Now);
}
public void OnSelectThisWeek(object sender, EventArgs e)
{
DateTime dt=DateTime.Now;
dateSelection.SetValue("EndDate", dt);
while (dt.DayOfWeek != 0)
{
dt-=oneDay;
}
dateSelection.SetValue("StartDate", dt);
}
public void OnSelectThisMonth(object sender, EventArgs e)
{
DateTime dt=DateTime.Now;
dateSelection.SetValue("EndDate", dt);
dt=new DateTime(dt.Year, dt.Month, 1, dt.Hour, dt.Minute, dt.Second);
dateSelection.SetValue("StartDate", dt);
}
public void OnSelectThisYear(object sender, EventArgs e)
{
DateTime dt=DateTime.Now;
dateSelection.SetValue("EndDate", dt);
dt=new DateTime(dt.Year, 1, 1, dt.Hour, dt.Minute, dt.Second);
dateSelection.SetValue("StartDate", dt);
}
public void OnSelectYesterday(object sender, EventArgs e)
{
DateTime dt=DateTime.Now;
dt=dt - oneDay;
dateSelection.SetValue("StartDate", dt);
dateSelection.SetValue("EndDate", dt);
}
public void OnSelectLastWeek(object sender, EventArgs e)
{
DateTime dt=DateTime.Now;
while (dt.DayOfWeek != 0)
{
dt-=oneDay;
}
dateSelection.SetValue("EndDate", dt);
dt-=new TimeSpan(7, 0, 0, 0);
dateSelection.SetValue("StartDate", dt);
}
public void OnSelectLastMonth(object sender, EventArgs e)
{
DateTime dt=DateTime.Now;
dt=new DateTime(dt.Year, dt.Month, 1, dt.Hour, dt.Minute, dt.Second);
dt=dt.AddMonths(-1);
dateSelection.SetValue("StartDate", dt);
dt=new DateTime(dt.Year, dt.Month, DateTime.DaysInMonth(dt.Year, dt.Month),
dt.Hour, dt.Minute, dt.Second);
dateSelection.SetValue("EndDate", dt);
}
public void OnSelectLastYear(object sender, EventArgs e)
{
DateTime dt=DateTime.Now;
dt=new DateTime(dt.Year, 1, 1, dt.Hour, dt.Minute, dt.Second);
dt=dt.AddYears(-1);
dateSelection.SetValue("StartDate", dt);
dt=new DateTime(dt.Year, 12, 31, dt.Hour, dt.Minute, dt.Second);
dateSelection.SetValue("EndDate", dt);
}
public void OnCustomDate(object sender, EventArgs e)
{
// if the container data is different from the control data, then the
// user is changing the control data.
string dtStart1=
((DateTime)dateSelection.GetContainerValue("StartDate")).ToShortDateString();
string dtStart2=
((DateTime)dateSelection.GetValue("StartDate")).ToShortDateString();
string dtEnd1=
((DateTime)dateSelection.GetContainerValue("EndDate")).ToShortDateString();
string dtEnd2=
((DateTime)dateSelection.GetValue("EndDate")).ToShortDateString();
if ( (dtStart1 != dtStart2) || (dtEnd1 != dtEnd2) )
{
dateSelection.SetValue("Option", -1);
}
}
}
]]>
</def:Code>
A Different View
Now, none of this would be of much value if I didn't demonstrate changing the
view without affecting the model or the controller. So, in this UI:

I have replaced the radio buttons with a ComboBox. Now, this
begs the question--where should we put the logic for figuring out which action
to take when the user makes a selection? Does this go in the View or the
Controller? There really isn't that good of an answer--in this particular case,
I've decided to demonstrate both. However, I feel that since the combobox is an
active selection that changes the UI state, the View should be responsible. The
View manages the list and applies meaning to the list contents, and it also
doesn't require changing the Controller code. I've demonstrated both approaches
in the download.
The elegance of separating the View from the Model/Controller is seen in the
next example. Swapping out:
<ComboBox def:Name="cbOption" Location="10, 20" Size="200, 20"
SelectionChangeCommitted="OnSelectChangeCommitted">
<Items>
<Item>Today</Item>
<Item>This Week</Item>
<Item>This Month</Item>
<Item>This Year</Item>
<Item>Yesterday</Item>
<Item>Last Week</Item>
<Item>Last Month</Item>
<Item>Last Year</Item>
</Items>
<DataBindings>
<MxDataBinding PropertyName="SelectedIndex" DataSource="DateSelection"
DataMember="Option"/>
</DataBindings>
</ComboBox>
with:
<ListBox def:Name="cbOption" Location="10, 20" Size="200, 80"
SelectedIndexChanged="OnSelectedIndexChanged">
etc...
and we have changed the View, independent of the Controller:

Conclusion
The MVC pattern is very powerful. By using declarative programming, a lot of
the drudgery in creating classes and wiring up events can be eliminated. The
examples I've illustrated here are somewhat trivial. In a complicated UI,
however, where a single Form may have multiple Views that are being
managed, or the selection in one View affects the state in another, this
technique becomes very useful. Also note that in the examples I've provided,
I've put the Model, View and Controller code in the same file as the markup.
This creates a physical binding between the three, which in real life you would
probably not do. Finally, this is very much a concept piece. I'm using this
technique for my own application development, and I suspect that it will mature
further as time goes on.
References