Click here to Skip to main content
12,898,662 members (58,443 online)
Click here to Skip to main content
Add your own
alternative version


97 bookmarked
Posted 13 Jun 2010

Developing and Unit Testing an ASP.NET MVC 2 Application

, 28 Jun 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
An introduction with AJAX, jQuery, JSON, MvcContrib and NUnit


One of the challenges of learning new software development technologies is finding a good working example that can guide you through the learning process. Most demonstrations I’ve seen through the years often show you a working example that front-ends the world famous Northwind demo database. Often, these demonstration applications show you a master-detail sales order GUI that uses a layer or two of plug-and-play plumbing. I’ve never really learned anything looking at these applications. What is really needed is something closer to a real world application that has just enough meat and potatoes to facilitate the learning process.

Sample Application


This article will demonstrate a sample medical application that includes functionality for adding, updating and searching patients. This application implements Microsoft’s new ASP.NET MVC 2 Framework using .NET 4.0. The Model-View-Controller (MVC) architectural pattern separates an application into three main components: the model, the view, and the controller. The ASP.NET MVC framework provides an alternative to the ASP.NET Web Forms pattern for creating Web applications. The ASP.NET MVC framework is a lightweight, highly testable presentation framework that (as with Web Forms-based applications) is integrated with existing ASP.NET features, such as master pages and membership-based authentication.

Solution and Projects

This sample application consists of four separate Visual Studio 2010 projects that are combined into one solution as follows:

  • MvcWebSite – This is the MVC web project that contains the Views and the Controllers. The Views will contain both JavaScript and HTML, and the Controllers will interact with the Data Model and the business logic layer.
  • DataModels - The DataModels project consists of the data entities that this application requires. The data model is separated into its own library so it may be shared across the enterprise.
  • CodeProjectBLL – The CodeProjectBLL DLL class library will contain all the necessary business logic layer and data access code. This layer is often called the Model.
  • CodeProjectUnitTests – This DLL class library will contain the unit tests that will be used for testing the application with the NUnit test runner.

The Data Model

Application development usually starts with defining your data entities and designing a database. For this sample application, the main data entity is patient information. After building the database tables for this application, the following class was created that exposes the underlying data model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DataModels
    /// <summary>
    /// Patient Data Model 
    /// </summary>
    public class Patient
        public long PatientID { get; set; }                 
        public string MedicalID { get; set; }        
        public string SocialSecurityNumber { get; set; }         
        public string FirstName { get; set; }                 
        public string LastName { get; set; }          
        public DateTime DateOfBirth { get; set; }    
        public DateTime EffectiveDate { get; set; }
        public string AddressLine1 { get; set; }                
        public string AddressLine2 { get; set; }        
        public string City { get; set; }        
        public string State { get; set; }               
        public string ZipCode { get; set; }          
        public string PhoneNumber { get; set; }

With the MVC framework as with other frameworks, it is tempting to just add your data model class under the Models folder of the MVC project. In the corporate world, you often have to develop and support more than one application that supports and shares the same data and accompanying data structures. For this reason, I like to create a separate project for all my data classes as a DLL library project so these classes can be shared across different applications across the corporate enterprise.

The View - Patient Maintenance and Search

At this point, I will walk through adding a patient from the Patient Maintenance View. PatientMaintenance.aspx is located in a folder under Views called Patient. Adding a new patient will require the user to enter a patient first name and last name, date of birth, a medical id, etc. Once this information is entered, the user can press the SAVE button.

<h3>Patient Information</h3>
<form method="post" action="./" id="PatientMaintenanceForm">
<table style="background-color: #ebeff2; width: 100%; border:solid 1px #9fb8e9" 

cellpadding="2" cellspacing="2">   
<tr><td> </td></tr>
 <td>Medical ID:</td>
 <td style="width:200px"><input name="MedicalID" type="text" id="MedicalID"  /></td>
 <td>Effective Date:</td><td>
 <input name="EffectiveDate" type="text" id="EffectiveDate"

    style="margin:0px 5px 0px 0px" />      
</td><td style="width:40%"> </td></tr> 
<td>First Name:</td>
<td style="width:200px"><input name="FirstName" type="text" id="FirstName" /></td>      
<td>Last Name:</td>
<td><input name="LastName" type="text" id="LastName"  /></td>
<td style="width:40%"> </td>
<td style="width:200px">
<input name="SocialSecurityNumber" type="text" id="SocialSecurityNumber"  /></td>       
<td>Date Of Birth:</td>
<td><input name="DateOfBirth" type="text" id="DateOfBirth" />
<td>Address Line 1:</td>
<td style="width:200px"><input name="AddressLine1" type="text" id="AddressLine1" />
<td>Address Line 2:</td>
<td><input name="AddressLine2" type="text" id="AddressLine2" /></td>
<td><input name="City" type="text" id="City"  /></td>        
<select id="State" name="State">
        <option value=""></option>           
<td>Zip Code:</td>
<td><input name="ZipCode" type="text" id="ZipCode"  /></td>       
<td>Phone Number:</td>
<td><input name="PhoneNumber" type="text" id="PhoneNumber"  /></td>
<td> </td>         
<table style="background-color:#D3DCE5; margin-top:10px; width:100% ">
<input id="btnSave" type="button" onclick="SavePatient();" value="Save" />    
<input id="btnReset" type="button" value="Reset" onclick="ResetPage();" />    
<br />
<div id="DivMessage"> </div>       

One of the nice features of MVC is that it provides a mechanism for binding your HTML form fields to a business model object through an UpdateModel command in the controller layer. In the above HTML patient form, I have given each textbox an id and name that matches the properties of my patient data model. When this form is submitted to the controller, the controller will bind and map the values of the form to the patient object.


jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. JQuery also has various plug-ins and widgets to enhance the development of the web UI. This sample application uses two jQuery UI add-ons, the date picker and the masked input plug-in. I downloaded these two plug-ins from and made a reference to the script files in the master page of this application. JQuery also comes with custom themes for look and feel. For the date picker, I chose to download the Pepper Grinder theme. The masked input plug-in allows you to format dates, phone number, social security numbers, etc. that are not available out of the box with MVC and standard HTML.

<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
link href="../../Content/css/PepperGrinder/jquery-ui-1.8.2.custom.css" 
    rel="stylesheet" type="text/css"  /> 
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>  
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.4.2.min.js" type="text/javascript"></script> 
<script src="../../Scripts/jquery.maskedinput-1.2.2.min.js"
<script src="../../scripts/jquery.ui.core.js" type="text/javascript"></script> 
<script src="../../scripts/jquery.ui.widget.js" type="text/javascript"></script>
<script src="../../scripts/jquery.ui.datepicker.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
 function pageReady() {
    $.ajaxSetup({ cache: false });
    formIsDisabled = false;                
    $(function () {
        $("#PatientMaintenanceForm #DateOfBirth").mask("99/99/9999");
        $("#PatientMaintenanceForm #EffectiveDate").mask("99/99/9999");
        $("#PatientMaintenanceForm #PhoneNumber").mask("(999) 999-9999");
        $("#PatientMaintenanceForm #SocialSecurityNumber").mask("999-99-9999");

    $(function () {$("#PatientMaintenanceForm #EffectiveDate").datepicker({ 
              showOn: 'button', 
              buttonImage: '../../content/cal.gif', 
              buttonImageOnly: true });                 

The jQuery Date Picker:


Submitting Data to the Server

When the user presses the Save button, the SavePatient JavaScript function gets executed in the browser. The SavePatient function uses the jQuery serialize method to convert the values of the HTML form fields into a notation that is based on the traditional HTML query string notation. This string is sent to the server when executing an HTML POST.

<script language="javascript" type="text/javascript"> 
function SavePatient() {
   formData = $("#PatientMaintenanceForm").serialize();
   setTimeout(PostChangesToServer, 1000); 
          // simulate a delay to the server

function PostChangesToServer() {
   $.post("/Patient/AddPatient", formData, 
               function (data, textStatus) {
                 }, "json");


Disabling the Form

function DisableForm() { 
     formIsDisabled = true;
     $('#PatientSearchForm :input').attr('disabled', true);
     $('#PatientSearchForm').css("cursor", "wait"); 
     $('#PatientMaintenanceForm :input').attr('disabled', true);
     $('#PatientMaintenanceForm').css("cursor", "wait");    

To submit the form data to the server, the application will use jQuery to execute an AJAX POST request to the server. Since the call to the server is asynchronous, the user could still perform other functions on this page during the submit request that may cause an undesirable situation. To prevent this, the form needs to be disabled. Most web sites often display a progress indicator to notify the user that the application is busy and the form is grayed out. I often find this approach less than idea and less esthetic in most cases. In this sample application, a more traditional approach is taken that is often seen in the desktop application world. The application simply changes the cursor to an hourglass and disables the buttons and links on the page.


Once the form is disabled, the jQuery POST method is executed. The POST method calls the server asynchronously with the supplied form field data. The MVC route of “/Patient/AddPatient” is referenced which is basically making a call to the PatientController class and executing the AddPatient method of the controller. When the request returns from the server, a JSON object is returned to the callback function “PatientUpdateComplete”.

JSON is a lightweight format for exchanging data between the client and server. It is often used in Ajax applications because of its simplicity and because its format is based on JavaScript object literals.

AddPatient Controller Method

/// <summary>
/// Add Patient
/// </summary>
/// <returns></returns>
public JsonResult AddPatient()
    bool returnStatus;
    string returnErrorMessage;      
    List<string> returnMessage; 
    PatientBLL patientBLL = new PatientBLL();
    Models.PatientViewModel patientViewModel = new Models.PatientViewModel();          
    Patient patient = patientBLL.AddPatient(
                out returnMessage, 
                out returnStatus, 
                out returnErrorMessage);
    patientViewModel.UpdateViewModel(patient, typeof(Patient).GetProperties());

    patientViewModel.ReturnMessage = returnMessage;
    patientViewModel.ReturnStatus = returnStatus;  
    return Json(patientViewModel); 

Another tempting thing to do with the MVC Framework is to put the business logic and data access code directly in the controller class. I strongly believe that all business logic and data access code should reside in separate projects outside the MVC project. This facilitates the sharing of application code across multiple business applications. It is also my belief that the controller class should be nothing more than a pass-through or a middleman between the View and the rest of the application. The controller should just bind data from the View to a business object and then execute the business logic layer code that resides outside the MVC project in a class library.

Patient View Model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DataModels;
using System.Reflection;
using CodeProjectBLL;
namespace MVCWebSite.Models
    public class PatientViewModel : DataModels.Patient
        public long CurrentPageNumber { get; set; }     
        public long PageSize { get; set; }      
        public long TotalPages { get; set; }
        public long TotalRows { get; set; }
        public List<string> ReturnMessage { get; set; }
        public bool ReturnStatus { get; set; }
        public string SortBy { get; set; }
        public string SortAscendingDescending { get; set; }             

The first thing that the AddPatient method does is instantiate the business logic layer class and a PatientViewModel class. The PatientViewModel class inherits from the Patient Data Model and includes additional properties that are needed for the View including properties for returning status and messages from the server.

Data Binding

The key line of code in the controller method is the TryUpdateModel statement. This statement will populate the properties in the PatientViewModel class with the values from the Patient Maintenance View. It does this based on mapping the data elements by name. As stated previously, the textbox objects in the View have the same property names in the PatientViewModel.

The AddPatient Controller method then proceeds to call the patientBLL.AddPatient method which inserts the patient information into a SQL Server database and then returns a new patient object with some reformatted data. In this case, the data entered is folded to uppercase, saved in the database and then returned back to the controller.

Update Patient View Model and Return JSON

When the patientBLL.AddPatient has successfully executed, the controller executes the UpdateViewModel method. This is a custom method that uses .NET Reflection to update the properties in the PatientViewModel from the patient object. This functionality is similar to the TryUpdateModel command and basically maps and updates property values by name in the other direction.

Finally, the AddPatient controller method returns the PatientViewModel object as a JSON object that can be parsed by the View. The nice thing is that MVC automatically translates your class object to a JSON object.

Update the View

<script language="javascript" type="text/javascript">
 function PatientUpdateComplete(jsonPatient) {
    firstName = $("#PatientMaintenanceForm #FirstName");
    if (jsonPatient.ReturnStatus == true) {
      $("#PatientMaintenanceForm #FirstName").val(jsonPatient.FirstName);
      $("#PatientMaintenanceForm #LastName").val(jsonPatient.LastName);
      $("#PatientMaintenanceForm #AddressLine1").val(jsonPatient.AddressLine1);
      $("#PatientMaintenanceForm #AddressLine2").val(jsonPatient.AddressLine2);
      $("#PatientMaintenanceForm #City").val(jsonPatient.City);
      $("#PatientMaintenanceForm #State").val(jsonPatient.State);
      $("#PatientMaintenanceForm #ZipCode").val(jsonPatient.ZipCode);
      $("#PatientMaintenanceForm #PhoneNumber").val(jsonPatient.PhoneNumber);
      $("#PatientMaintenanceForm #SocialSecurityNumber").val(
      $("#PatientMaintenanceForm #MedicalID").val(jsonPatient.MedicalID);
      $("#PatientMaintenanceForm #DateOfBirth").val(
      $("#PatientMaintenanceForm #EffectiveDate").val(
      $("#PatientMaintenanceForm #PatientID").val(jsonPatient.PatientID);

    var returnMessage = ""; 
    for ( i=0; i<jsonPatient.ReturnMessage.length; i++ )
        returnMessage = returnMessage + jsonPatient.ReturnMessage[i] + "<br>";
    if (jsonPatient.ReturnStatus == true) {
          $("#DivMessage").css("background-color", "#ebeff2");
          $("#DivMessage").css("border", "solid 1px #9fb8c9");
          $("#DivMessage").css("color", "#36597f");
          $("#DivMessage").css("padding", "5 5 5 5"); 
    else {
          $("#DivMessage").css("background-color", "#f4eded");
          $("#DivMessage").css("border", "solid 1px #d19090");
          $("#DivMessage").css("color", "#762933");
          $("#DivMessage").css("padding", "5 5 5 5"); 

As previously stated, the server returns a JSON object back to the JavaScript callback function PatientUpdateComplete. In the example above, jQuery is used to parse the JSON object and update the form fields in the View and checks and displays return status information. Basically, this example made a round trip AJAX call using jQuery.

function EnableForm() {                
    formIsDisabled = false;  
    $('#PatientSearchForm  :input').removeAttr('disabled');
    $('#PatientSearchForm').css("cursor", "default");    
    $('#PatientMaintenanceForm  :input').removeAttr('disabled');
    $('#PatientMaintenanceForm').css("cursor", "default");            

Finally, the last step is to enable the HTML form to allow the user to continue.

Unit Testing

One of the main areas that is cited as a benefit of using the new ASP.NET MVC Framework is that you can obtain a 'clean separation of concerns'. What this means is that your application code is now more testable compared to ASP.NET Web Forms. MVC controllers replace Web Form code-behind files. Since controllers are standard class objects, they can now be unit tested directly with a unit testing tool.

To unit test this sample application, I downloaded three open source projects: NUnit, MVCContrib and Rhino Mocks:

  • NUnit is an open source unit-testing framework for all .NET languages and can be downloaded from
  • MVCContrib is also an open source project for the ASP.NET MVC framework. This project adds additional functionality on top of the MVC Framework. This sample application will use the TestHelper library for unit testing. Download it from
  • Rhino Mocks is a dynamic mock object framework for the .NET platform. Its purpose is to ease testing by allowing the developer to create mock implementations of custom objects and verify the interactions using unit testing. MVCContrib uses this framework when using the TestHelper library. Download it from

To test the AddPatient Controller method, I created a method in my CodeProjectUnitTests project called Test_AddPatient and made a reference to the NUnit and MVCContrib frameworks.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using MvcContrib.TestHelper;
using MVCWebSite;
using MVCWebSite.Models;
using MVCWebSite.Controllers;
using System.Web.Mvc;
using System.Collections.Specialized;
using System.Configuration;
using DataModels;
namespace CodeProjectUnitTests
    /// <summary>
    /// Unit Tests
    /// </summary>
    public class UnitTests
        /// <summary>
        /// Test Add Patient
        /// </summary>
        public void Test_AddPatient()

            TestControllerBuilder builder = new TestControllerBuilder();

            string uniquePatientKey = GenerateUniqueID();
            builder.Form["MedicalID"] = uniquePatientKey;
            builder.Form["SocialSecurityNumber"] = uniquePatientKey;
            builder.Form["FirstName"] = "William";
            builder.Form["LastName"] = "Gates";
            builder.Form["AddressLine1"] = "Microsoft Corporation";
            builder.Form["AddressLine2"] = "One Microsoft Way";
            builder.Form["City"] = "Redmond";
            builder.Form["State"] = "WA";
            builder.Form["ZipCode"] = "98052-7329";
            builder.Form["PhoneNumber"] = "(425)882-8080";
            builder.Form["DateOfBirth"] = "10/28/1955";
            builder.Form["EffectiveDate"] = "01/01/1975";
            PatientController patientController = 
            JsonResult jsonResult = (JsonResult)patientController.AddPatient();
            dynamic jsonData = jsonResult.Data; 
            Assert.AreEqual(jsonData.ReturnStatus, true);
            Assert.Greater(jsonData.PatientID, 0);                            

The first thing that the test method above does is create a TestControllerBuilder object. This object is part of the MVCContrib library under the MvcContrib.TestHelper namespace. The TestControllerBuilder not only creates an instance of the controller, but it also mocks out the HttpContext, Session and form data. It also provides access to MVC ViewData, TempData, etc.

The unit test above mocks form data, creates the controller, and executes the AddPatient method of the Patient controller. The controller returns JSON which can be inspected and tested by the test method using NUnit assert commands. Since the properties of the JSON object are anonymous at compile time, the test method declares the data from the JSON object as dynamic as introduced in C#. This means I can reference the properties but these properties will not be evaluated or resolved until runtime execution.



Loading the CodeProjectUnitTests assembly into the NUnit test runner, you can now execute your unit tests running directly against the MVC controller class outside and without IIS or the ASP.NET development server running.

Additional Functionality - Patient Searching

This sample application also includes a second HTML form demonstrating patient search functionality that returns and builds an HTML grid that includes support for paging and sorting.

/// <summary>
/// Patient Search
/// </summary>
/// <returns></returns>
public PartialViewResult PatientSearch()
      long totalRows;
      long totalPages;
      bool returnStatus;
      string returnErrorMessage;
      PatientBLL patientBLL = new PatientBLL();

             patientViewModel = new Models.PatientViewModel();           

      List<Patient> patients = patientBLL.PatientSearch(
                out totalRows,
                out totalPages,
                out returnStatus,
                out returnErrorMessage);
      ViewData["patients"] = patients;

      patientViewModel.TotalPages = totalPages;
      patientViewModel.TotalRows = totalRows;
      ViewData.Model = patientViewModel;
      return PartialView("PatientSearchResults");

When hitting the SEARCH button in the View, the PatientSearch method of the controller class is executed through an jQuery Ajax call. The business logic layer returns a generic list of patient objects that are stored in the ViewData object. The ViewData object is the standard way of passing application data to the View.

The paging functionality is controlled by passing in the values for the current page number and the page size. Only a single page of patient objects is returned from the business layer based on these two parameters. After the first page is rendered, the user can press the next page link which increments the current page number by one to get the next set of patients from the database.

In this example, a JSON object is not passed back to the View, This time, a partial view result is being returned. The goal is to dynamically generate a grid using a MVC view user control named “PatientSearchResults.ascx” as follows:

<table><tbody> <% 
int i = 0;
foreach (var patient in (IEnumerable<DataModels.Patient>)ViewData["patients"])
{ %> 
<tr style="height:25px; color:Black; 
background-color:<%= i++ % 2 == 0 ? "#D3DCE5" : "#ebeff2" %>"> 
<td style="width: 20%">
<%= Html.Encode(patient.MedicalID)%>
<td style="width: 20%">
<%= Html.Encode(patient.SocialSecurityNumber)%>
<td style="width: 20%">
<a href="javascript:GetPatientDetails('<%= Html.Encode(patient.PatientID)%>');">
<%= Html.Encode(patient.LastName)%></a> 
<td style="width: 20%">
<%= Html.Encode(patient.FirstName)%>
<td style="width: 40%">
<% } %>

The view user control generates the needed HTML for the grid by iterating through the generic list of patients in the ViewData Model object. All this HTML is then passed back to the original JavaScript call:

 function PostSearchToServer() {
    $.post('/Patient/PatientSearch', formData, function (returnHtml) {
          currentPageNumber = $("#PatientSearchForm #CurrentPageNumber").val();
          totalPages = $("#PatientSearchForm #TotalPages").val();
          if (totalPages > 0) 
              SetPagingLinks(currentPageNumber, totalPages);

When the HTML is returned from the view user control, the DIV tag inside the PatientSearch.ascx” view user control is dynamically updated with the returned HTML.

<div id="DivPatientSearchResults">
 <% Html.RenderPartial("PatientSearchResults",Model); %>

For those who have experience with classic ASP development prior to the arrival of Microsoft .NET, will find the <% %> syntax familiar. When MVC 1.0 was initially released, I was not too thrilled to go back to this coding style. But as I reacquainted myself with the syntax, I was able to move forward with the technology. Later I would discover the different ways of limiting this server side coding style by either using jQuery to parse JSON objects, generating the HTML from a class library or implementing one of the various alternative View Engines that are available to you. Most notably, I was impressed with the Spark View Engine ( The idea behind the Spark View Engine is to allow the HTML to dominate the flow of the View and allow the server side code to fit seamlessly.

One of the nice things I also like about the MVC framework is that you can now have more than one form on your page, compared to ASP.NET Web Forms which only allowed you to have one form per page. Having multiple forms on your web page allows you to simulate AJAX update panels and partial page postback functionality similar to Web Forms. By overriding the ACTION attribute of the HTML form through JavaScript, you can create very complex pages that can postback to various different MVC controllers and methods.


This was just a introductory example of developing a web application using the MVC framework. The MVC framework is rich and robust. There are many areas of the MVC framework that were not addressed in this article. The MVC framework also comes with various helper classes for generating HTML objects. You can also apply attributes and filters to your controller methods that can be executed before and after your controller method executes. In the end, you end up with greater control of the HTML that gets sent down to the browser and more importantly, the MVC framework allows for greater separation of concerns through the different layers of an application which facilitates the automation of unit testing.


  • Microsoft Visual Web Developer 2010 Express
  • Microsoft ASP.NET MVC 2.0
  • Microsoft .NET C# 4.0
  • Microsoft SQL Server 2008 R2 Express Edition
  • NUnit 2.5.5
  • MVCContrib
  • Rhino Mocks


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Mark J. Caplin
Software Developer Caplin Systems
United States United States
Mark Caplin has specialized in Information Technology solutions for 25 years. Specializing in full life-cycle development projects for both enterprise-wide systems and Internet/Intranet based solutions.

For the past ten years or so, Mark has specialized in the Microsoft .NET framework using both C# and VB.NET as his tools of choice.

Mark currently is the Chief Solutions Architect at Joey Software Solutions, Inc. Visit Joey Software at

When not coding, Mark enjoys playing tennis, listening to U2 music, watching Miami Dolphins football and watching movies in Blu-Ray technology.

In between all this, his wife of over 20 years, feeds him well with some great home cooked meals.


You may also be interested in...


Comments and Discussions

GeneralMy vote of 5 Pin
Monjurul Habib17-Jun-11 10:22
memberMonjurul Habib17-Jun-11 10:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170424.1 | Last Updated 28 Jun 2010
Article Copyright 2010 by Mark J. Caplin
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid