Until last year, I had never used an MVP/MVC pattern before.  I had read about the patterns at Martin Fowler's site, although the Model View Presenter pattern has since been retired.  I had also seen some posts and demos and talks where people were using MVC/MVP frameworks.  But I kept thinking there was something missing.  I couldn't put my finger on it for a long time, but I just had that gut feeling that something wasn't covered.

Last year though, I was assigned to a new project, for the first time in a few years.  The new project was going to be WinForms.  After getting up to speed on requirements, I started thinking pretty hard about using MVP for the project.  Once I focused on it, I figured out what I thought was missing from everything I had seen about MVP (and MVC).

Here's a diagram right from Martin Fowler's site:

This shows an interface that includes actualValue, varianceValue and varianceColor.  These are scalar values that can be injected into the View.  Dealing with scalar values seemed to be commonplace throughout everything I had seen using MVC or MVP.  The controller or presenter can update the view's scalar values, and the view displays those values.

One problem stuck out to me like a sore thumb.  When editing a record, how does the presenter tell the view if a certain field is not editable?  After asking some folks that I know that had been using MVC or MVP, they replied with a rather simple solution.  The view interface defines a boolean field for whether or not the field is enabled.  The view then uses that to enable/disable the field appropriately.  Others stated they used events to manage this.  And in both cases, the presenter must trust that the view handles it.

This wasn't good enough for me; I was thinking bigger.  How is client-side validation wired up?  When server-side validation occurs, how does the presenter know how to generate the error message if only the view knows how the field was presented to the user?

In order to answer these questions, I thought about how I've addressed those problems in a Web Forms system.  For me, that's easy: Custom Web Controls Everywhere!  I've been preaching custom controls "everywhere" for a long time now.  Heck, I gave that presentation nearly 2½ years ago, and the concept was old to me then.

At BIG, I developed a TextBox control that we've used on several projects.  The control can validate itself with any of the ASP.NET validators, in a very simple fashion:

 1: <BIG:TextBox Runat="server" ID="txtHomePhone"
 2: FieldName="Home Number"
 3: Required-Enabled="True"
 4: Format="Phone" />

This would result in a TextBox that has the correct max length, size, regular expression validator, required field validator, an error indicator consistent with the rest of the application, error messages stating "Phone Number is required" and "Home Phone is not a valid phone number."  Additionally, the presentation of the textbox is altered to indicate that it's required.  The DASL project renders a little lightning bolt icon next to any required textbox.  The control actually has dozens of other features too, but we'll stop there.

On another project here at BIG, Ben Von Handorf created something he called "Reflections on Validation."  He presented this at CINNUG only a few months after I presented custom web controls everywhere.  Ben had taken my textbox to the next level by wiring it all the way into the business layer.  Now, the textbox became as simple as this:

 1: <BIG:TextBox Runat="server" ID="txtHomePhone"
 2: FieldName="Home Phone"
 3: Property="HomePhone" />

The rest of the stuff on the textbox got wired up from his object validation engine in the business layer, with only a small hook from the UI layer.  He used custom attributes to create validation attributes like this:

 1: [Validation.Required]
 2: [Validation.Format(Validation.Formats.Phone)]
 3: public string HomePhone
 4: {
 5: get { return _homePhone; }
 6: set { _homePhone = value; }
 7: }

Now, that's just brilliant.  Or, at least it was back in 2005!  It's commonplace now, and I like to think Ben was one of the pioneers behind this.

I wanted all of this goodness in my MVP pattern.  What I ended up coming up with was a totally new (as far as I know) flavor of MVP.  This pattern doesn't pass scalar values around; instead it deals with control interfaces.  It can best be explained by showing a sample view interface:

 1: public ICustomerEditView
 2: {
 3: [Validation.Required]
 4: ITextBox txtFirstName;
 6: ITextBox txtMiddleName;
 8: [Validation.Required]
 9: ITextBox txtLastName;
 11: ITextBox txtAddress;
 12: ITextBox txtCity;
 13: IComboBox cboState;
 14: ITextBox txtZipCode;
 15: ICheckBox chkOnMailingList;
 16: }

That's right, instead of exposing the fields from the view as scalar values, let's expose them as control interfaces.  We can then build out our control interfaces to support whatever we need the presenter to manage for us.  The first thing we add is ITextBox.Text as a string.  So we can still manipulate the scalar value, but through its control.  Then for any form field, we expose an Enabled property (via an IFormField interface of course), and the presenter can manage whether or not the field is editable.

Now, it could probably go without saying that in order to implement this, you have to employ custom controls everywhere.  But that's okay, because I do that anyway.  Even when I don't know of any functionality to add to a control, or any presentation or behavior to override, I inherit the stock class, just in case.  So now, I inherit from the stock TextBox control, and I implement ITextBox.  I like the side effect here that this really forces you to use custom controls everywhere.

Once I really started thinking about the idea, it became very easy to solve some otherwise difficult problems, before I ever wrote a single line of code.  For instance, how does the presenter know how to format an error message if the view was responsible for rendering the form?

A simple example is when you allow a customer to put 2 phone numbers on the form.  Let's assume the following form content:

 1: <tr>
 2: <td class="FormLabel"><BIG:Label Runat="server" ID="lblHomePhone" Text="Home Phone" />:</td>
 3: <td class="FormField"><BIG:TextBox Runat="server" ID="txtHomePhone" LabelID="lblHomePhone" /></td>
 4: </tr>

(yeah, yeah, I still use table-based layouts, but hey, they still work)

Notice the textbox references the label?  What ends up happening is ITextBox contains a field for FieldLabel as an ILabel.  The TextBox custom control implementation takes the LabelID value, finds the ILabel control that it's referencing, and exposes that control as ITextBox.FieldLabel.

Our presenter is now empowered with the following capability:

 1: errorMessages.Add(string.Format("{0} is required", txtHomePhone.FieldLabel.Text));
 2: errorMessages.Add(string.Format("{0} is not a valid phone number", txtHomePhone.FieldLabel.Text));

Now that you can see the power that is available to us, I'm going to leave some things to your imagination for awhile.  But I'll give you some hints about where this framework went.  We have reflection-based validation and security (including view- and field-level security), controls automatically reformat themselves based on what validation and security rules have been applied, and we have tooltips on the field labels that show a summary of what rules have been applied.  This ended up being the friendliest UI I've ever built--it tells the user everything about what's going on.

I will write more about this, but please feel free to hit me with whatever questions or comments you have.  And in the coming weeks, I hope to try to bolt this framework on top of the ASP.NET MVC framework coming out, to see how well it plugs in.

It feels great to finally get this written too!  I've been wanting to write this post for about 16 months, but I've never found the time before.

Also, I provided ASPX samples, but each of the projects on which we've implemented this framework has been WinForms.  It's just easier to show sample ASPX markup.  And besides, the pattern will work for either type of project in the same way--only the custom controls would be different.

Technorati Tags: , , , , ,