Model *View* Controller

A lot is being made of Microsoft’s Model View Controller (or MVC) framework at the moment.

I have only spent a couple of days working with it so far but it does look promising. If you are a TDD convert or just interested in solid OOP principles such as separation of concerns it may well be right up your street.

As with all change, the emergence of this new approach to asp.net programming has caused its fair share of controversy and debate but that’s for another day. Lets just say there are those who are yet to be convinced of it’s value and those who are already sold on it.

For today though I want to focus on the View part of the equation.

Interestingly I think Scott Guthrie may have had a bit of a revelation one day when he was posting about MS AJAX on his blog. Whether MVC was already in development or not, this post does seem to lean neatly into what we’re now all talking about.

Cool UI Templating Technique to use with ASP.NET AJAX for non-UpdatePanel Scenarios 

I have recently adapted the technique mentioned in that article for an AJAX web site. Lets look at some code (heavily based on Scott’s original post).

  1. public static string RenderView(string path, object[] data)
  2.   {
  3.       HtmlForm tempForm = new HtmlForm();
  4.  
  5.       Page pageHolder = new Page();
  6.       pageHolder.EnableEventValidation = false;
  7.       pageHolder.EnableViewState = false;
  8.  
  9.       UserControl viewControl = (UserControl) pageHolder.LoadControl(path);
  10.  
  11.       if (data != null)
  12.       {
  13.           Type viewControlType = viewControl.GetType();
  14.           PropertyInfo propertyInfo = viewControlType.GetProperty("Data");
  15.  
  16.           if (propertyInfo != null)
  17.           {
  18.               propertyInfo.SetValue(viewControl, data, null);
  19.           }
  20.           else
  21.           {
  22.               throw new Exception("View file: " + path + " does not have a public Data property");
  23.           }
  24.       }
  25.  
  26.       tempForm.Controls.Add(viewControl);
  27.       pageHolder.Controls.Add(tempForm);
  28.  
  29.       StringWriter output = new StringWriter();
  30.  
  31.       try
  32.       {
  33.           HttpContext.Current.Server.Execute(pageHolder, output, false);
  34.       }
  35.       catch (Exception ex)
  36.       {
  37.           Debug.WriteLine(ex.Message);
  38.           throw;
  39.       }
  40.  
  41.       string outputToReturn = output.ToString();
  42.  
  43.       outputToReturn = outputToReturn.Substring(outputToReturn.IndexOf("<div>"));
  44.       outputToReturn = outputToReturn.Substring(0, outputToReturn.IndexOf("</form>"));
  45.  
  46.       int StartPoint = outputToReturn.IndexOf("<input type=\"hidden\" name=\"__VIEWSTATE\"");
  47.       if (StartPoint >= 0)
  48.       {
  49.           int endPoint = outputToReturn.IndexOf("</div>", StartPoint) + 6;
  50.           outputToReturn = outputToReturn.Substring(endPoint);
  51.       }
  52.  
  53.       return outputToReturn;
  54.   }

This function is pretty straightforward. Given a path to a user control and some data in the form of an object array it will attempt to render the user control and spit out an html string.

NB: To avoid tripping up asp.net we wrap our user control contents in a form prior to rendering, then remove the form afterwards.

Expose this as a method in a class (ViewManager) and you’re ready to go.

Now to an example of a view.

  1. <table>
  2.     <tr>
  3.         <td>
  4.             <b>Customer Name:</b>
  5.         </td>
  6.         <td>
  7.             <%=data.Name %>
  8.         </td>
  9.     </tr>
  10.     <tr>
  11.         <td>
  12.             <b>Date:</b>
  13.         </td>
  14.         <td>
  15.             <%=DateTime.Now%>
  16.         </td>
  17.     </tr>
  18.     <tr>
  19.         <td>
  20.             <b>Date Registered</b>
  21.         </td>
  22.         <td>
  23.             <%=data.DateRegistered%>
  24.         </td>
  25.     </tr>
  26. </table>

Note the strongly typed access to the data’s fields. This is possible due to the code-behind for this view.

  1. public Customer data;
  2.  
  3. public object[] Data
  4. {
  5.     get { return new object[] {data}; }
  6.     set { data = value[0] as Customer; }
  7. }

I have explicitly stated the type of the data object (Customer).

You’ll also note I have used an object array rather than a single object. This just adds a bit of flexibility as you can pass in more than one object to bind the view to.

Interestingly ASP.NET MVC supports strongly typed data access using generics (example below)…

  1. using System.Web.Mvc;
  2. using Shop.Data.Model;
  3.  
  4. namespace PartsMatching.App.Views.Rule
  5. {
  6.     public partial class CustomerDetails : ViewPage<Customer> {}
  7. }

The real beauty of this templating approach though (and what Scott originally alluded to) is that you can render this html and then view it however you want. Once class I’ve used sends the rendered view out as an email.

With MS AJAX I’ve used a ModalPopup control from the ASP.NET AJAX Control Toolkit to display the html.

  1. function ShowPopup(contents) {
  2.     var infoPanel = $get('modalContents');
  3.     infoPanel.innerHTML = contents;
  4.     $find("ModalPopup").show();
  5. }

The similarities between this approach and MVC are fairly obvious. You can have controllers which render the view using the RenderView method and then do what you like with the resulting HTML.