Home

Form Basics

A web framework would be nothing without easy and flexible form handling. Vici MVC includes very flexible and easy to use form handling, without using the infamous ViewState used in ASP.NET WebForms.

How forms work in Vici MVC

In HTML, a form is a collection of controls defined inside a <form> section. A form can be posted to the server by clicking on a "submit" button.

To retrieve the contents of a posted form, you can can read the PostData collection, which maps directly to the ASP.NET Request.Form collection. While this is an easy way to retrieve form data, it becomes complicated if you want to set the initial values of controls in a form, and maintain the state (value) of controls across postbacks.

In Vici MVC, this is handled by the WebForm class. You simply create a WebForm-derived class and add members representing the data you want to show on (and retrieve from) the HTML form. You tell Vici MVC what type of control should be rendered for the member, and optionally what kind of validation should be done.

As always, an example tells more than a thousand words, so here is a simple one:

public class FormSampleController : PageController
{
   public class SampleForm : WebForm
   {
       [ValidateLength(3)]
       [FormTextBox(MaxLength=30)]
       public string EmployeeName;
 
       [FormTextBox(Min=0)]
       public decimal Salary;
   }
 
   public void Run(int id)
   {
        SampleForm form = new SampleForm();
 
        form.Bind();
 
        if (form.Validated)
        {
            // do something with the posted data
            
            ViewData["Result"] = "Name entered: " + form.EmployeeName + ", Salary=" + form.Salary;
        }
   }
}

The associated view template looks something like this:

<html>
  <body>
    <form method="post" action="{{CurrentUrl}}">
      <label>Employee name</label>
      [[EmployeeName]]
      <label>Salary</label>
      [[Salary]]
      <div><input type="submit" name="btnSave" value="Save" /></div>
     </form>
     <!--{{ if Result }}--><div>Result: {{Result}}</div><!--{{ endif }}-->
  </body>
</html>

What happens in the controller's action method is:

  • A WebForm-derived object is created which contains 2 fields that are represented as textboxes
  • The textboxes are empty by default
  • The form is bound to the current view by calling the Bind() method
  • The Validated property will return true when the form is posted to the controller action, and the validations were successful
  • The first time the controller action is executed, there is no "postback", so the Validated property will be false, and the form will be rendered
  • When the user clicks on the submit button, the action method is executed again, but this time the Bind() method will retrieve the values of the textboxes as entered by the user
  • The Validated property will return true (if the user entered valid data of course), and the ViewData["Result"] will be set.

Setting the initial values of the controls

To set the initial values of the form, you should override the OnFill() method of the WebForm class. This method will be called the first time the form is shown, and when no postback is performed:

public class SampleForm : WebForm
{
   [ValidateLength(3)]
   [FormTextBox(MaxLength=30)]
   public string EmployeeName;

   [FormTextBox(Min=0)]
   public decimal? Salary;

   protected override void OnFill()
   {
        EmployeeName = "Some default value"; // or a value retrieved from a database
        Salary = null; // setting it null will make the field blank
   }
}

Retrieving user input (postback)

You should aways let the HTML form post to the original URL. This way the WebForm-derived object will correctly handle the data entered by the user and state will be preserved.

To capture posted data, you don't have to do anything. You only have to check the Validated property of the WebForm class. If it is true, the form was posted and the input was validated according to the validation rules (attributes) that were defined for each field.

Another way of capturing posted fields is by overriding the OnPost() method. This method has one parameter validated, which is set to true if all validations were successful. You would typically use this scenario when you bind a form object to a data object that was read from the database:

public class SampleForm : WebForm
{
   private Employee _employee; // not mapped to a control because there's no attribute

   [ValidateLength(3)]
   [FormTextBox(MaxLength=30)]
   public string EmployeeName;

   [FormTextBox(Min=0)]
   public decimal? Salary;

   public SampleForm(Employee employee)
   {
      _employee = employee;
   }

   protected override void OnFill()
   {
        EmployeeName = _employee.Name;
        Salary = _employee.Salary;
   }

   protected override OnPost(bool validated)
   {
        _employee.Name = EmployeeName;
        _employee.Salary = Salary;
   }
}

Then, in your controller, you simply attach an employee record to your form and save the record when the Validated proprty of the form is true:

public class FormSampleController : PageController
{
   public void Run(int id)
   {
      Employee employee = DataLayer.ReadEmployee(id);

      SampleForm form = new SampleForm(employee);
 
      form.Bind();
 
      if (form.Validated)
      {
         employee.Save();

         Response.Redirect("...some other place...");
      }
   }
}

Note that in version 2.0 of Vici MVC there is no automatic data binding. You should set the form's fields to values from a data object in code. This is a feature that will likely be added in the next release of the framework.

Input validation

As mentioned above, Vici MVC performs validation on form objects. There are 3 ways to validate a form, and they can be combined at will:

  • Validation attributes on fields
  • Validation methods for specific fields
  • Validation method for the complete form

Validation is discussed in the section about Form validation.

Supported controls

  • [FormTextBox]
  • [FormDropdown]
  • [FormPassword]
  • [FormEMail]
  • [FormHidden]
  • [FormCheckbox]
  • [FormDate]
  • [FormMemo]
  • [FormRadioButton]

These are just the built-in controls. You can create your own controls which can be bound to form fields.

For an explanation of the built-in controls, check the section on Vici MVC controls

Binding data to list controls

So far we have only used simple text boxes as controls, but what if you have a dropdown control or another control that needs additional data?

For that purpose, there's another method you can override: OnBind(). This method is called just before the page is rendered. This also means that the fields in the form class already have the correct value (either initialized by OnFill or submitted by the user) and the actual controls have been created also.

Every control has a DataSource property, which should be set to the data required by the control. For a listbox this is the list of items that should be displayed in the listbox.

For example:

public class SampleForm : WebForm
{
   [FormTextBox(MaxLength=30)]
   public string EmployeeName;

   [FormDropDown(KeyMember="CountryID", ValueMember="CountryName", ShowBlank=true, BlankKey=0)]
   public int CountryCode;

   protected override OnBind()
   {
      Fields["CountryCode"].DataSource = DataLayer.GetCountries();
   }
}

If you want to access the control directly, you can read the Control property of the field:

public class SampleForm : WebForm
{
   [FormTextBox(MaxLength=30)]
   public string EmployeeName;

   [FormDropDown(KeyMember="CountryID", ValueMember="CountryName", ShowBlank=true, BlankKey=0)]
   public int CountryCode;

   protected override OnBind()
   {
      DropdownControl countryControl = (DropdownControl) Fields["CountryCode"];
        
      countryControl.DataSource = DataLayer.GetCountries();
   }
}

Styling controls with CSS

As explained earlier, control placeholders are represented in the followin form

[[ControlName]]

This is the basic notation, but there's more. The full syntax of the control placeholder is:

[[ControlName:CssClass:CssClassError]]

What happens is that the control will be rendered using the specified CSS class "CssClass" when the initial form is rendered, and when the control is valid (validation succeeded).

When validation fails, the control will be rendered with the CSS class "CssClassError". This CSS class will be used instead of the normal CSS class. If you want to add a class to the "normal" CSS class, you should prepend the class name with "+".

Examples:

UsageNormal CSS classCSS class for invalid fields
[[Control:TextBox]]TextBoxTextBox
[[Control:TextBox:TextBoxError]]TextBoxTextBoxError
[[Control::Error]] Error
[[Control:TextBox:+Error]]TextBoxTextBox Error