Grid

The Grid component allows for the easy construction of HTML tables for displaying data from a collection of Model objects.

The following blog posts provide an introduction to the Grid component.

Updated documentation will be available shortly

Introduction

The Grid component provides a strongly-typed way to build an HTML table using a fluent interface and lambda expressions. The Grid component is located in the MvcContrib.UI.Grid namespace and can be accessed by calling the Html.Grid extension method.

Basic Syntax

To define a basic grid you'll need to call the Html.Grid extension method from within your view. There are two overloads for this method - the first takes a strongly-typed collection and the second takes a viewdata key as a string.

For example, imagine that you have a controller action which passes a strongly-typed collection to the View's Model property:

public ActionResult Index() {
  IEnumerable<Customer> customers = customerRepository.FindAll();
  return View(customers);
}


...then you can pass the collection directly to the grid:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Customer>> %>
<%@ Import Namespace="MvcContrib.UI.Grid" %>

<%= Html.Grid(Model) ... %>


Alternatively, you might choose to use ViewData:

public ActionResult Index() {
  IEnumerable<Customer> customers = customerRepository.FindAll();
  ViewData["customers"] = customers;
  return View();
}


...in which case you can can pass the ViewData key to the Grid method. In this case, you also have to specify the type of collection in the ViewData by using a generic type parameter:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="MvcContrib.UI.Grid" %>

<%= Html.Grid<Customer>("customers") ... %>


Next, you can use the Columns method to define which properties on your object should be rendered as columns in the grid. In this case, our customer object is defined as:

public class Customer {
  public int Id { get; set; }
  public string Surname { get; set; }
  public string Forename {get; set; }
  public DateTime DateOfBirth { get; set; }
}


If we wanted to generate columns for three of these properties, the grid definition would look like this:

<%= Html.Grid(Model).Columns(column => {
          column.For(cust => cust.Id); 
          column.For(cust => cust.Surname);
          column.For(cust => cust.DateOfBirth);
}) %>


Note that we use a nested closure which can be used to configure the underlying ColumnBuilder.

In this case, the grid will contain a column for each of these three properties. The column heading will contain the name of the property. For PascalCased properties, the column heading will contain additional spaces (eg DateOfBirth would become "Date Of Birth")

Custom Columns

Columns can also contain arbitrary output - they do not have to be tied to a single property. For example, you could define a composite column that concatenates the Surname and Forename properties of our Customer object.

column.For(cust => cust.Surname + " " + cust.Forename).Named("Customer Name");


Note that in this case we concatenate the Surname and Forename properties within the column expression. In this case, the Grid will not be able to infer the column heading, so it must be explicitly specified by calling the Named method.

This can also be taken a stage further. For example, we could construct some custom HTML from within this expression:

column.For(cust => Html.ActionLink("View Details", "Show", new{ id = cust.Id })).Named("View Details").DoNotEncode();


In this case, we use the standard ASP.NET MVC Html.ActionLink helper to generate a link to the "Show" action on our controller. Note the call to DoNotEncode at the end of the column definition. By default, all column output is HTML-encoded, but in this case we want to bypass the HTML encoding as we want to display custom HTML in the column.

Note The call to DoNotEncode is unnecessary using the latest source-code drop if the contents of the column column returns an instance of MvcHtmlString. However, the current release build still requires this call.

Column options

Columns can specify additional options which are configured by chaining additional methods to the end of column.For(...).

Option Description Example
Named Provides a custom display name for a column column.For(x => x.Name).Named("Customer Name")
DoNotSplit By default, all PascalCased property names are split (so "DateOfBirth" would become "Date Of Birth"). This option disables this behavior. column.For(x => x.DateOfBirth).DoNotSplit()
Format Provides a custom formatting string for a cell value column.For(x => x.DateOfBirth).Format("{0:d}")
CellCondition Accepts a function that specifies whether a particular cell should be rendered column.For(x => x.Salary).CellCondition(x => User.Identity.IsInRole("Admin"))
Visible Controls whether an entire column should be shown. The default is true. column.For(x => x.Salary).Visible(User.Identity.IsInRole("Admin"))
Encode Controls whether the contents of a cell should be encoded. Note This option is new and is not contained in the current release - use "DoNotEncode" instead. column.For(x => x.SomeProperty).Encode(false)
HeaderAttributes Provides additional custom attributes for the column header. This can be provided either as a dictionary or by using a Hash column.For(x => x.Surname).HeaderAttributes(new Dictionary<string, object> { { "class", "foo" } }) OR column.For(x => x.Surname).HeaderAttributes(@class => "foo")
Attributes Provides custom attributes for a cell. This can be provided either as a dictionary or by using a Hash column.For(x => x.Surname).Attributes(x => new Dictionary<string, object> { { "class", "foo" } }) OR column.For(x => x.Surname).Attributes(@class => "foo")
Sortable Specifies whether a column is sortable. This only applies if sorting is enabled (see below). The default is true column.For(x => x.Id).Sortable(false)
SortColumnName Specifies a custom column name for use with sorting (see below) column.For(x => x.Surname).SortColumnName("CustomerName")
InsertAt Specifies the order of the column when overriding auto-generated columns (see below). This option is new and is not included in the latest release. column.For(x => x.Surname).InsertAt(0)


Automatically generated columns

If you are using a dedicated presentation model with your view, then the AutoGenerateColumns method can be used to automatically generate a column for each public property on your model rather than having to explicitly specify each column.

<%= Html.Grid(Model).AutoGenerateColumns() %>


Under the covers, the AutoGenerateColumns method uses the MVC2 ModelMetadata provider to work out how the properties should be rendered. As such, the DataAnnotations attributes can be used to customise how auto-generated columns should be rendered. At present, the following attributes are supported:

- ScaffoldColumn - can be used to hide a particular column (equivalent of column.Visible(false))
- DisplayName - can be used to provide a custom column name (equivalent of column.Named("foo"))
- DisplayFormat - can be used to provide a custom format for a column (equivalent of column.Format("{0:some format}"))

You can add additional columns after the automatically generated columns by calling the Columns method:

<%= Html.Grid(Model).AutoGenerateColumns().Columns(extraCols => {
  extraCols.For(x => Html.ActionLink("View Details", "Show", new { id = cust.Id }).Named("Show Details")
}) %>


You can also re-arrange the additional columns. For example, if you wanted your extra column to appear before the auto-generated columns then you can use the InsertAt method to specify an index. Note This is a new feature that is not in the latest release, so if you want to make use of this then you will need to either build from source or download a nightly build.

<%= Html.Grid(Model).AutoGenerateColumns().Columns(extraCols => {
  extraCols.For(x => Html.ActionLink("View Details", "Show", new { id = cust.Id })
    .Named("Show Details")
    .InsertAt(0);
}) %>


Sorting

Unlike the ASP.NET Web Forms GridView control, the MvcContrib grid does not perform any sorting of your data itself. However, it can be used to render data that has been sorted in your controller action (or a service layer).

To enable sorting, you first need to take a GridSortOptions object in your controller action:

public ActionResult Index(GridSortOptions sort) {
  //...
}


This object contains two properties (Column and Direction) that can be used to sort your data. MvcContrib contains an additional overload for Linq's OrderBy method which accepts a column name and a sort direction, but use of this method is optional - you can use whatever mechanism you like for performing the sort.

The GridSortOptions instance will then need to be passed to the view in order to tell the grid how the data has been sorted.

using MvcContrib.Sorting;
using MvcContrib.UI.Grid;

public class CustomerController : Controller {
  private CustomerRepository customerRepository = new CustomerRepository(); 
  
  public ActionResult Index(GridSortOptions sort) {
     var customers = customerRepository.FindAll();
     if(sort.Column != null) {
        customers = customers.OrderBy(sort.Column, sort.Direction);
     }
    ViewData["sort"] = sort;
    return View(customers);
  } 
} 


In the view, the Sort method should be called on the grid. This method accepts an instance of GridSortOptions so that the grid knows how the data has been sorted and can therefore work out how the sorting links in the column headers should be generated.

<%= Html.Grid(Model).AutoGenerateColumns().Sort((GridSortOptions)ViewData["sort"]) %>


At this point, the Grid will now add hyperlinks in the column headings. The hyperlink will contain two querystring parameters - Column and Direction. These will be used by the MVC Model-binding infrastructure in order to create the GridSortOptions object that is passed to the controller action.

When the data has been sorted on a column, the grid will place additional CSS classes in the column headings. If the column is currently sorted in ascending order then the appropriate <th> tag will have a class of sort_asc. Likewise, if the column is sorted in descending order then the css class will be sort_desc. These classes can be used to add additional styling (for example, to add up/down arrows). Please look at the MvcContrib.Examples.UI project for a complete example.

Paging

Similar to the sorting example, the grid does not actually perform any paging internally but it can be used to render pre-paged data. To enable this, you'll need to take an additional argument for your controller action called "page":

public ActionResult Index(int? page) {
   //...
}


This argument will be used to specify which page of data should be rendered. As with the sorting example, you are free to use whatever mechanism you wish to perform the paging, but MvcContrib does provide an extension method on IEnumerable called AsPagination*. The only requirement is that the result of performing the paging returns an implementation of the IPagination interface.

using MvcContrib.Pagination;

public class CustomerController : Controller {
  private CustomerRepository customerRepository = new CustomerRepository();
  
  public ActionResult Index(int? page) {
     var pagedCustomers = customerRepository.FindAll().AsPagination(page ?? 1, 10);
     return View(pagedCustomers);
  }


In this example, we pass the page number to the AsPagination method (defaulting to page 1 if no page was specified in the querystring) with a page size of 10.

The grid requires no modifications in order for this to work - the IPagination interface also implements IEnumerable, so the grid can process this as with any other collection.

However, in order to render paging links on the page, you can make use of the Html.Pager extension method which is in the MvcContrib.UI.Paging namespace:

<%= Html.Pager((IPagination)Model) %>


TODO: Write documentation on the additional options available on the Pager.

Grid Models

TODO

Grid Renderers

TODO

Action Syntax

Last edited May 15, 2010 at 9:11 PM by JeremyS, version 24

Comments

ngruson Nov 23, 2012 at 5:54 AM 
How can I handle editing data using the Grid? I'm binding the grid to a collection which is a property of my model. When I edit data in the grid, how is the changed data reflected back to the model? Have been searching for an example, but couldn't find any.

AIVolkov Aug 17, 2012 at 6:04 AM 
It there a way to use DisplayFor templates instead of simple ToString cell-rendering?
Like this http://stackoverflow.com/questions/2877439/mvccontrib-grid-and-display-edit-templates

ibsukru Apr 3, 2012 at 2:55 PM 
Totally bad practice

rstackhouse Oct 21, 2011 at 9:13 PM 
This line is wrong as of the current version of MvcContrib on NuGet:

"However, in order to render paging links on the page, you can make use of the Html.Pager extension method which is in the MvcContrib.UI.Paging namespace"

MvcContrib.UI.Paging should read MvcContrib.UI.Pager.

Sosh Feb 18, 2011 at 5:10 AM 
I think there is a typo in the 2nd code example in the 'Automatically generated columns' section - should be another closing parenthesis on line 2.

JeremyS Feb 9, 2009 at 10:25 AM 
If you have questions/comments please use the Mailing List (http://groups.google.com/group/mvccontrib-discuss) rather than posting comments on the wiki. Thanks!