UserControls with the MVC Framework

Published 3/11/2008 by Henry in C# | Patterns
Tags: ,

When building applications re-using components that contain functionality that is used in more than one place in that application is best practice. More than that it is one of the fundamentals of OO development.

What about with the MVC framework?
Can we use Controls, or UserControls? Because the MVC Framework does not use ViewState, events can not be used, so how do we use these Controls with the MVC Framework?
With the MVC Framework at this moment in time we can use the MVCToolkit, this Toolkit has UIHelpers, with these UIHelpers it is easier to create Textboxes, Dropdownlists, RadioButtons etc in the View.
But what if we have a grid with the same datasource and columns that we need on different Views? How do we go about in creating say a custom 'GridControl' UserControl that we can use on one or more in our MVC Framework application?

Creating a custom Grid UserControl
First we create a new MCV View User Control Item, through a right-click 'Add > New Item...'.  

New MVC View User Control
New MVC View User Control

We name the Control: 'Grid'. Than we open the Code-Behind Grid.cs file and you will see that the UserControl does not inherit System.Web.UI.UserControl, but inherits from System.Web.Mvc.ViewUserControl.
Because we want to show data in a grid, we need a way to let the Control use the same data as the containing ViewPage.
For this purpose we can use (like the System.Web.Mvc.ViewPage) the System.Web.Mvc.ViewUserControl syntax to provide our control with the object that we want to bind the grid to.

   1:  using System;
   2:  using System.Web;
   3:  using System.Web.Mvc;
   4:  using HC.Data.AuditHistory;
   5:   
   6:  namespace HC.Web.AuditTool.Views.Controls
   7:  {
   8:      public partial class Grid : ViewUserControl<AuditData>
   9:      {
  10:      }
  11:  }
Listing 1

All we have to do now is make sure our grid looks oke and shows the right data in the right place:

   1:  <table cellpadding="0" cellspacing="0" class="ListArea">
   2:      <tr>
   3:          <td id="tdDataArea">
   4:              <table id="gridBodyTable" cellpadding="1" cellspacing="0" class="List-Data">
   5:                  <colgroup>
   6:                      <col class="DataColumn List-SortedColumn"   />
   7:                      <col class="DataColumn"  />
   8:                      <col class="DataColumn"  />
   9:                  </colgroup>                                               
  10:  <% 
  11:      int numberOfRow = 0;
  12:      foreach (var auditHistory in ViewData.AuditHistory)
  13:      {
  14:  %>                                                         
  15:                  <tr class="List-Row">
  16:                      <td class="DataCell">
  17:                          <%=auditHistory.FieldDisplayName%>
  18:                       </td>
  19:                      <td class="DataCell">
  20:                          <nobr >
  21:                              <%=auditHistory.PreData == "" ? "&nbsp;" : auditHistory.PreData%>
  22:                          </nobr>
  23:                      </td>
  24:                      <td class="DataCell">
  25:                          <nobr >
  26:                              <%=auditHistory.UserName%>
  27:                          </nobr>
  28:                      </td>
  29:                  </tr>                                                 
  30:  <%
  31:      }
  32:  %>
  33:              </table>
  34:          </td>
  35:      </tr>
  36:  </table>
Listing 2

Now we open the first ViewPage in designmode and drag and drop the UserControl on the ViewPage. In the ViewPage we also make sure we inherit from ViewPage and use the ViewPage syntax:

   1:  using System.Web.Mvc;
   2:  using HC.Data.AuditHistory;
   3:   
   4:  namespace HC.Web.AuditTool.AuditTool.Views.Auditing
   5:  {
   6:      public partial class List : ViewPage<AuditData>
   7:      {
   8:      }
   9:  }
Listing 3

In our Controller we fill the ViewData with data and call the View, so the ViewPage loads and holds the ViewData. Next the UserControl is loaded by the ViewPage and it's ViewData is filled by the ViewPage's ViewData.

   1:  using System.Web.Mvc;
   2:   
   3:  using HC.Data.AuditHistory;
   4:  using HC.Web.AuditTool.Models;
   5:   
   6:  public class AuditingController : Controller
   7:  {
   8:      [ControllerAction]
   9:      public void Index()
  10:      {
  11:          AuditData auditData = AuditingDataModel.GetAuditData();
  12:          ViewData.Add("AuditHistory", auditData);
  13:          RenderView("List", auditData);
  14:      }
  15:  }
Listing 4

The result is a grid on a web page.
But the beaty is that whenever a view(page) uses the same object for data, the grid will work. 

I hope I made clear that with the MVC Framework we lose ViewState and events, but we can still re-use our Controls, be it in a slightly different way than we are used to.

Henry Cordes
My thoughts exactly...