This is the second part of Organizing view models in ASP.NET MVC (see Part 1, Part 2).

In this part I’ll give a sample of designing view models for handling forms in MVC. Suppose that we need to create a controller and stuff around it to handle form like the following.

For the sake of demo simplicity I keep the form simple:

For this, we add View that specifies AccountRegisterViewModel as its model:

@model AccountRegisterViewModel
 
<h2>
    Register</h2>
 
@using (Html.BeginForm())
{
    @Html.ValidationSummary()
    <label>
        Name
        @Html.TextBoxFor(m => m.Form.Name)
    </label>
    <label>
        State
        @Html.DropDownListFor(m => m.Form.State, Model.StateSelectList)
    </label>
    <input type="submit" value="Register me" />
}

Then, we add a new view model class to ViewModels/Account directory. It contains all the data required to render the form, including drop down list data, and properties of the form to submit (in this sample we have a single form under Form property, but if you have multiple forms, then you can place each one under separate FormXXX property):

public class AccountRegisterViewModel : SharedLayoutViewModel
{
    public AccountRegisterForm Form { get; set; }
 
    public IEnumerable<SelectListItem> StateSelectList { get; set; }
}

We define a class that contains all the fields of our form. Later, we’ll be able to use strongly-typed helpers to render inputs. The class contains DataAnnotations attributes for form validation, and for other cases it can implement the IValidatableObject interface. So we have all the object level validation for the form in a single place. Nice and clean:

public class AccountRegisterForm
{
    [Required]
    public string Name { get; set; }
 
    [Required]
    public string State { get; set; }
}

The controller itself looks like this:

[HttpGet]
public ActionResult Register()
{
    var model = new AccountRegisterViewModel();
 
    SetupRegisterViewModel(model);
    return View(model);
}
 
[HttpPost]
public ActionResult Register([Bind(Include = "Form")]AccountRegisterViewModel model)
{
    if (ModelState.IsValid)
    {
        // save model.Form 
 
        TempData["Message"] = "Thank you for registering";
        return RedirectToAction("Index");
    }
 
    SetupRegisterViewModel(model);
    return View(model);
}
 
private void SetupRegisterViewModel(AccountRegisterViewModel model)
{
    model.StateSelectList = new SelectList(new[] { string.Empty, "NY", "VA" });
}

That’s it.